﻿#include "patterns.h"

PatternManager patterns;

// ==================  Эксперименты с BulletML =======================
//void BulletCommand::doVanish() {
//	bullet_->active = 0;
//}
//
//void BulletCommand::createSimpleBullet(double direction, double speed) {
//	Bullet new_bullet;
//	new_bullet = *bullet_;
//	new_bullet.behaviour = patterns.GetBulletBehaviour("bullet_simple");
//	new_bullet.speed = speed;
//	new_bullet.orientation = CL_Angle(direction, cl_degrees);
//	new_bullet.direction = CL_Vec2f(cos(new_bullet.orientation.to_radians()), sin(new_bullet.orientation.to_radians()));
//	bmanager.Create(new_bullet);
//}
//
//void BulletCommand::createBullet(BulletMLState* state, double direction, double speed) {
//	Bullet new_bullet;
//	new_bullet = *bullet_;
//	new_bullet.behaviour = patterns.GetBulletBehaviour("bullet_simple");
//	new_bullet.speed = speed;
//	new_bullet.orientation = CL_Angle(direction, cl_degrees);
//	new_bullet.direction = CL_Vec2f(cos(new_bullet.orientation.to_radians()), sin(new_bullet.orientation.to_radians()));
//	bmanager.Create(new_bullet);
//}
//
//void BulletCommand::doChangeDirection(double direction) {
//	bullet_->orientation = CL_Angle(direction, cl_degrees);
//	bullet_->direction = CL_Vec2f(cos(bullet_->orientation.to_radians()), sin(bullet_->orientation.to_radians()));
//}
//
//void BulletCommand::doChangeSpeed(double speed) {
//	bullet_->speed = speed;
//}
//
//void BulletCommand::doAccelX(double accel) {
//}
//
//void BulletCommand::doAccelY(double accel) {
//}
//
//double BulletCommand::getBulletSpeed() {
//	return bullet_->speed;
//}
//
//double BulletCommand::getBulletSpeedX() {
//	return bullet_->speed * bullet_->direction.x;
//}
//
//double BulletCommand::getBulletSpeedY() {
//	return bullet_->speed * bullet_->direction.y;
//}
//
//double BulletCommand::getDefaultSpeed() {
//	return bullet_->start_param_float1;
//}
//
//double BulletCommand::getBulletDirection() {
//	return bullet_->orientation.to_degrees();
//}
//
//double BulletCommand::getAimDirection() {
//	CL_Angle a_to_player = patterns.get_bullet_orientation(patterns.get_direction_to_player(bullet_->position));
//	return a_to_player.to_degrees();
//}
//
//double BulletCommand::getRank() {
//	return 0;
//}
//
//int BulletCommand::getTurn() {
//	return pattern_->cur_turn;
//}
//
//int BulletCommand::getEndTurn() {
//	return pattern_->bullet_time1;
//}
//
//BulletCommand::BulletCommand(BulletMLParser* bp, Bullet* b)
//    : BulletMLRunner(bp)
//{
//	bullet_ = new Bullet;
//	*bullet_ = *b;
//}
//
//BulletCommand::BulletCommand(BulletMLState* bs, Bullet* b)
//    : BulletMLRunner(bs)
//{
//	bullet_ = new Bullet;
//	*bullet_ = *b;
//}
//
//BulletCommand::~BulletCommand()
//{
//	delete bullet_;
//}

// ==========================================================

Behaviour::Behaviour()
{
}

Behaviour::~Behaviour()
{
}

bool Behaviour::InsideScreen(CL_Vec2<float> position)
{
	//return visible_rect.contains(position);
	return global_state.game_field.contains(position);
}

bool Behaviour::InsideScreen(float pos_x, float pos_y)
{
	//return visible_rect.contains(CL_Vec2<float>(pos_x, pos_y));
	return global_state.game_field.contains(CL_Vec2<float>(pos_x, pos_y));
}

Effect_Behaviour::Effect_Behaviour()
{
}

Effect_Behaviour::~Effect_Behaviour()
{
}

Enemy_Path::Enemy_Path()
{
}

Enemy_Path::~Enemy_Path()
{
}

bool Enemy_Path::InsideScreen(CL_Rectf position)
{
	return visible_rect.is_overlapped(position);
}

bool Enemy_Path::InsideScreen(CL_Vec2<float> position)
{
	return global_state.game_field.contains(position);
}

bool Enemy_Path::InsideScreen(float pos_x, float pos_y)
{
	return global_state.game_field.contains(CL_Vec2<float>(pos_x, pos_y));
}

// ---
PatternManager::PatternManager()
{
}

PatternManager::~PatternManager()
{
	for (std::map<std::string, Behaviour*>::iterator it = bullet_behaviours.begin(); it != bullet_behaviours.end(); it++)
		delete it->second;
	for (std::map<std::string, Enemy_Path*>::iterator it = enemy_paths.begin(); it != enemy_paths.end(); it++)
		delete it->second;
}

// Загрузить параметры пуль и паттернов из ресурсов
void PatternManager::Load(CL_ResourceManager &resources, CL_VirtualDirectory &vdir)
{
	// Пули
	std::vector<CL_String> resource_names = resources.get_resource_names_of_type("bullet");
	for (size_t i = 0; i < resource_names.size(); i++)
	{
		CL_Resource bul = resources.get_resource(resource_names[i]);
		Bullet t_bul;
		t_bul.appearance = renders.GetBulletRender(bul.get_element().get_child_string("render"));
		t_bul.collision_size = CL_StringHelp::text_to_float(bul.get_element().get_child_string("collision"));
		t_bul.damage = CL_StringHelp::text_to_int(bul.get_element().get_child_string("damage"));
		bullet_templates[resource_names[i].c_str()] = t_bul;
	}
	// Паттерны
	resource_names.clear();
	resource_names = resources.get_resource_names_of_type("pattern");
	for (size_t i = 0; i < resource_names.size(); i++)
	{
		CL_Resource p = resources.get_resource(resource_names[i]);
		Pattern t_ptn;
		t_ptn.bullet_behaviour = GetBulletBehaviour(p.get_element().get_child_string("behaviour"));
		t_ptn.bullet_number = CL_StringHelp::text_to_int(p.get_element().get_child_string("bullet_number"));
		t_ptn.bullet_speed = CL_StringHelp::text_to_float(p.get_element().get_child_string("speed"));
		t_ptn.bullet_time1 = CL_StringHelp::text_to_float(p.get_element().get_child_string("time1"));
		t_ptn.bullet_time2 = CL_StringHelp::text_to_float(p.get_element().get_child_string("time2"));
		t_ptn.bullet_param1 = CL_StringHelp::text_to_float(p.get_element().get_child_string("param1"));
		t_ptn.bullet_start_param1 = CL_StringHelp::text_to_float(p.get_element().get_child_string("start_param1"));
		t_ptn.pattern_speed = CL_StringHelp::text_to_float(p.get_element().get_child_string("pattern_speed"));
		t_ptn.pattern_param1 = CL_StringHelp::text_to_float(p.get_element().get_child_string("pattern_param1"));
		t_ptn.pattern_param2 = CL_StringHelp::text_to_float(p.get_element().get_child_string("pattern_param2"));
		t_ptn.pattern_param3 = CL_StringHelp::text_to_float(p.get_element().get_child_string("pattern_param3"));
		t_ptn.pattern_param4 = CL_StringHelp::text_to_float(p.get_element().get_child_string("pattern_param4"));
		t_ptn.pattern_param5 = CL_StringHelp::text_to_float(p.get_element().get_child_string("pattern_param5"));
		t_ptn.bullet_effect = p.get_element().get_child_string("effect");
		t_ptn.bullet_effect_index = CL_StringHelp::text_to_int(p.get_element().get_child_string("effect_index"));
		pattern_templates[resource_names[i].c_str()] = t_ptn;
	}
	// Спеллкарты
	resource_names.clear();
	resource_names = resources.get_resource_names_of_type("spellcard");
	for (size_t i = 0; i < resource_names.size(); i++)
	{
		spellcards[resource_names[i].c_str()] = new Spellcard_Work(resources, resource_names[i]);
	}
	// BulletML
	//bmlparser = new BulletMLParserTinyXML(std::string("test"), std::string(vdir.open_file_read("Patterns/test.xml").read_string_text(NULL, NULL).c_str()));
	//bmlparser->build();
}


// Создать все доступные паттерны
// Пули:
//  - Behaviour_Simple				- "bullet_simple"						- летит по прямой с заданной скоростью
//  - Behaviour_Simple2				- "bullet_simple2"					- летит по прямой, меняя скорость через некоторое время
//  - Behaviour_Simple3				- "bullet_simple3"					- летит по прямой, постепенно меняя скорость до заданного значения
//  - Behaviour_Arc						- "bullet_arc"							- летит с угловой скоростью, меняя скорость через заданный интервал
//  - Behaviour_Arc_Line				- "bullet_arc_line"					- летит с угловой скоростью, выходя на касательную через заданный интервал
//  - Behaviour_Arc_Polar			- "bullet_arc_polar_player1"		- в полярных координатах, летит по спирали
//  - Behaviour_Player_Bullet1		- "bullet_player_1"					- спеллкарта Перекрестье
// Враги:
//  = Enemy_Path_Straight				- "enemy_straight"			- летит по прямой с заданной скоростью
//  = Enemy_Path_Move_Aside		- "enemy_go_left"			- уходит влево
//  = Enemy_Path_Stop_Wait_Move	- "enemy_wait_5s"			- останавливается, ждёт 5 секунд, улетает
//  = Enemy_Path_Go_Wait_Go		- "enemy_stopgo_5s"		- останавливается, ждёт 5 секунд, двигает дальше
//  = Enemy_Path_Boss					- "enemy_boss0"				- поведение босса
// Эффекты:
//  * Effect_Behaviour_Stay_Still		- "effect_stay_still"	- неподвижный эффект
//  * Effect_Behaviour_Score_Float	- "score_float"		- всплывающие цифры очков
//  * Effect_Behaviour_Flyoff			- "effect_flyoff"		- движется по прямой
void PatternManager::Init(CL_Rectf rect)
{
	bullet_behaviours["bullet_simple"] = new Behaviour_Simple(rect);
	bullet_behaviours["bullet_simple2"] = new Behaviour_Simple2(rect, 300);
	bullet_behaviours["bullet_simple2_1"] = new Behaviour_Simple2(rect, 150);
	bullet_behaviours["bullet_simple3"] = new Behaviour_Simple3(rect, -0.00001f, 0.03f);
	bullet_behaviours["bullet_arc"] = new Behaviour_Arc(rect, 0, 1);
	bullet_behaviours["bullet_arc_line"] = new Behaviour_Arc_Line(rect, 500, 1.2f);
	bullet_behaviours["bullet_arc_polar_player1"] = new Behaviour_Arc_Polar(rect, player.shot3_accel_time, player.shot3_speed_delta);
	//bullet_behaviours["bullet_player_1"] = new Behaviour_Player_Bullet1(CL_Rectf(-100.0f,0,650.0f,700.0f), player.shot4_radius, player.shot4_change_angle, player.shot4_speed_delta);
	bullet_behaviours["bullet_player_1"] = new Behaviour_Player_Bullet2(CL_Rectf(-100.0f,0,650.0f,700.0f), player.shot4_change_angle, player.shot4_speed_delta);
	bullet_behaviours["bullet_lazor1"] = new Behaviour_Lazor();
	bullet_behaviours["bullet_controlled1"] = new Behaviour_Controlled1(rect);
	bullet_behaviours["bullet_timed"] = new Behaviour_Timed(rect);
	bullet_behaviours["bullet_stopping"] = new Behaviour_Stopping(rect);
	bullet_behaviours["bullet_turning"] = new Behaviour_Turning(rect);

	// Крутящаяся отлетающая пуля
	bullet_behaviours["bullet_spinning"] = new Behaviour_Spinning(rect);

	enemy_paths["enemy_straight"] = new Enemy_Path_Straight(rect);
	enemy_paths["enemy_go_left"] = new Enemy_Path_Move_Aside(rect, 0.2f, true);
	enemy_paths["enemy_go_right"] = new Enemy_Path_Move_Aside(rect, 0.2f, false);
	enemy_paths["enemy_wait_2s"] = new Enemy_Path_Stop_Wait_Move(rect, 2000);
	enemy_paths["enemy_wait_5s"] = new Enemy_Path_Stop_Wait_Move(rect, 5000);
	enemy_paths["enemy_wait_10s"] = new Enemy_Path_Stop_Wait_Move(rect, 10000);
	enemy_paths["enemy_stopgo_1s"] = new Enemy_Path_Go_Wait_Go(rect, 1000);
	enemy_paths["enemy_stopgo_2s"] = new Enemy_Path_Go_Wait_Go(rect, 2000);
	enemy_paths["enemy_stopgo_5s"] = new Enemy_Path_Go_Wait_Go(rect, 5000);
	enemy_paths["enemy_stopgo_10s"] = new Enemy_Path_Go_Wait_Go(rect, 10000);
	enemy_paths["enemy_one_point_stop1"] = new Enemy_Path_One_Point_Stop(rect, 150);
	enemy_paths["enemy_sinus1"] = new Enemy_Path_Sinus(8);
	enemy_paths["enemy_sinus2"] = new Enemy_Path_Sinus(6);

	enemy_paths["enemy_boss0"] = new Enemy_Path_Boss(rect, 200);

	effect_behaviours["effect_stay_still"] = new Effect_Behaviour_Stay_Still();
	effect_behaviours["score_float"] = new Effect_Behaviour_Score_Float();
	effect_behaviours["effect_flyoff"] = new Effect_Behaviour_Flyoff();
	effect_behaviours["effect_grav"] = new Effect_Behaviour_Grav(0.01);

	_patterns.reserve(2000);

	player_shot_sound_time = 0;
	pcount=0;
}

// Получить данные из разных хранилищ
Behaviour* PatternManager::GetBulletBehaviour(CL_String name)
{
	return bullet_behaviours[name.c_str()];
}

Effect_Behaviour* PatternManager::GetEffectBehaviour(CL_String name)
{
	return effect_behaviours[name.c_str()];
}

Enemy_Path * PatternManager::GetEnemyPath(CL_String name)
{
	return enemy_paths[name.c_str()];
}

Spellcard_Work * PatternManager::GetSpellcard(CL_String name)
{
	return spellcards[name.c_str()];
}

// -------------------------------------------------------

// Запуск нового паттерна
void PatternManager::LaunchPattern(CL_Vec2<float> from_position, int type, Enemy* l_enemy)
{
	CL_SharedPtr<Pattern> new_pattern(new Pattern);
	// Взять настройки пуль из темплейта
	CL_String pname = CL_String("p") + CL_StringHelp::int_to_text(type / 1000);
	*new_pattern = pattern_templates[pname.c_str()];
	// ---
	new_pattern->active = 1;
	new_pattern->enemy_object = l_enemy;
	new_pattern->type = type;
	new_pattern->time_working = 0;
	new_pattern->sound_time = 70.0f;
	new_pattern->fired = 0;
	new_pattern->active_bullets = 0;
	new_pattern->trigger = 0;
	new_pattern->start_position = from_position;
//	new_pattern->cur_turn = 0;
	pcount++;
	new_pattern->id = pcount;
	//cl_log_event("Debug", "New pattern id %1 type %2 created! Patterns total : %3", pcount, type, _patterns.size());
	//cl_log_event("Debug", "Pattern id %1 : speed %2 time1 %3 time2 %4", pcount, new_pattern->bullet_speed, new_pattern->bullet_time1, new_pattern->bullet_time2);
	_patterns.push_back(new_pattern);
}

// Убрать все работающие паттерны
void PatternManager::Clear()
{
	_patterns.clear();
}

// ================================================================
// =   Работа паттернов. Все паттерны собраны в одной функции.
// ================================================================

void PatternManager::Update(float time)
{
	size_t t=0;
	for (t=0;t<_patterns.size();++t)
	{
		Pattern &p = *_patterns[t];
		// Обработка каждого типа
		if (p.type < 100000000)
		{
			// Паттерны обычных врагов
			// Проверить, жив ли выпустивший нас объект
			if (!p.enemy_object || !p.enemy_object->alive || p.enemy_object->dead)
			{
				p.active = 0;
				continue;
			}
			// Обработчики по типу паттерна
			int pat_type = p.type / 1000;
			// -------------------------- прямая пуля -------------------------------------------------
			// Pattern Type  0 - 29
			if (pat_type < 30)
			{
				// Обычный выстрел одной пулей в сторону игрока
				// Меняется вид пули в записимости от типа
				// Меняется поведение пули в зависимости от типа - что стоит в темплейте
				// Сначала запускается эффект выстрела, потом сама пуля
				// Положение не следует за врагом
				if (p.time_working == 0)
				{
					// Сначала запускаем эффект выстрела
					CreateEffect(p.bullet_effect, p.start_position, p.type % 20);
					// И звук выстрела
					smanager.PlaySound("enemy_shot1");
				}
				p.time_working += time;
				// Запускаем пулю через time1 после выстрела, после отработавшего эффекта
				if (p.time_working > p.bullet_time1)
				{
					Bullet new_bullet = GetBulletTemplate(p, p.start_position);
					new_bullet.direction = get_direction_to_player(p.start_position);
					new_bullet.orientation = get_bullet_orientation(new_bullet.direction);
				
					float update_time = p.time_working - p.bullet_time2;
					if (update_time > 0)
					{
						new_bullet.time_lived = 0; // Для обновления
						new_bullet.Update(update_time);
					}
					bmanager.Create(new_bullet);
					p.active = 0;
				}
			}
			// ---------------------------  одновременные прямые пули  ------------------------------------------------
			// Pattern Type  30 - 99
			else if (pat_type < 100)
			{
				// Выстрел одновременно несколькими пулями
				// Меняется вид пули в записимости от типа
				// Сначала запускается эффект выстрела, потом сами пули
				// Положение не следует за врагом
				if (p.time_working == 0)
				{
					// Сначала запускаем эффект выстрела
					CreateEffect(p.bullet_effect, p.start_position, 3);
					// И звук выстрела
					smanager.PlaySound("enemy_shot1");
				}
				p.time_working += time;
				// Запускаем пулю через time1 после выстрела, после отработавшего эффекта
				if (p.time_working > p.bullet_time1)
				{
					float update_time = p.time_working - p.bullet_time2;
					for (int t = 0; t < p.bullet_number; t++)
					{
						Bullet new_bullet = GetBulletTemplate(p, p.start_position);
						new_bullet.direction = get_direction_to_player(p.start_position);
						new_bullet.orientation = get_bullet_orientation(new_bullet.direction);
						new_bullet.speed = p.bullet_speed + ((float)t / 100);
						new_bullet.param_float1 = p.bullet_param1 + ((float)t / 15);
				
						new_bullet.time_lived = 0; // Для обновления
						new_bullet.Update(update_time);
						bmanager.Create(new_bullet);
					}
					p.active = 0;
				}
			}
			// -----------------------------------  кольцо из пуль  ------------------------------------------------
			// Pattern Type  100 - 149
			else if (pat_type < 150)
			{
				// выпускает кольцо из пуль или несколько колец с разной скоростью
				// Bullet_number показывает количество колец 
				// bullet_time1 - дельта скорости каждой следующей пули
				smanager.PlaySound("enemy_shot2");
				// Количество пуль в выстреле
				for (float a = 0; a<360.0f; a+=360.0f / p.pattern_param2)
				{
					CL_Angle an(a + p.pattern_param1, cl_degrees);
					CL_Vec2<float> dirc(cos(an.to_radians()), sin(an.to_radians()));
					CreateEffect(p.bullet_effect, p.start_position + dirc * 2, p.type % 20);
					for (int t = 0; t < p.bullet_number; t++)
					{
						Bullet new_bullet = GetBulletTemplate(p, p.start_position);
						new_bullet.direction = dirc;
						new_bullet.orientation = an;
						new_bullet.speed = p.bullet_speed + ((float)t * p.bullet_time1);
						new_bullet.param_float1 = p.bullet_param1 + ((float)t * p.bullet_time1);
						bmanager.Create(new_bullet);
					}
				}
				p.active = 0;
			}
			// --------------------------------------  кольцо из множества пуль  ---------------------------------------------
			// Pattern Type  150 - 199
			else if (pat_type < 200)
			{
				// Выпускает кольцо из множества пуль
				// Пули располагаются по окружности от спрайта выпустившего
				// bullet_number - количество пуль в выстреле
				if (p.time_working == 0)
				{
					//float range = p.enemy_object->collision_size * 3.0f;
					float range = p.pattern_param2;
					for (float a = 0; a<360.0f; a+=360.0f / (float)p.bullet_number)
					{
						CL_Angle an(a + p.pattern_param1, cl_degrees);
						CL_Vec2<float> dirc(cos(an.to_radians()), sin(an.to_radians()));
						CreateEffect(p.bullet_effect, p.start_position + (dirc * range), p.bullet_effect_index);
					}
				}
				p.time_working += time;
				if (p.time_working > p.bullet_time1)
				{
					//float range = p.enemy_object->collision_size * 3.0f;
					float range = p.pattern_param2;
					for (float a = 0; a<360.0f; a+=360.0f / (float)p.bullet_number)
					{
						CL_Angle an(a + p.pattern_param1, cl_degrees);
						CL_Vec2<float> dirc(cos(an.to_radians()), sin(an.to_radians()));
						Bullet new_bullet = GetBulletTemplate(p, p.start_position);
						new_bullet.position = p.start_position + dirc * range;
						new_bullet.direction = dirc;
						new_bullet.orientation = an;
						bmanager.Create(new_bullet);
					}
					smanager.PlaySound("enemy_shot2");
					p.active = 0;
				}
			}
			// --------------------------------------  очередь из пуль по кругу ---------------------------------------------
			// Pattern Type  250 - 299
			else if (pat_type < 300)
			{
				// Очередь из пуль, точка выпуска крутится вокруг противника
				// bullet_number - количество пуль в выстреле
				// time1 - период выпуска
				// pattern_speed - угловая скорость вращения "дула"
				p.time_working += time;
				while ((p.bullet_time1 * p.fired < p.time_working) && (p.fired < p.bullet_number))
				{
					CL_Angle an(p.time_working * p.pattern_speed, cl_degrees);
					CL_Vec2<float> start_pos = p.enemy_object->position + CL_Vec2<float>(cos(an.to_radians()) * p.pattern_param1, sin(an.to_radians()) * p.pattern_param1);
					CreateEffect(p.bullet_effect, start_pos, p.bullet_effect_index);
					float update_time = p.time_working - (p.bullet_time1 * p.fired);
					Bullet new_bullet = GetBulletTemplate(p, start_pos);
					new_bullet.direction = get_direction_to_player(start_pos);
					new_bullet.orientation = get_bullet_orientation(new_bullet.direction);
					new_bullet.Update(update_time);
					bmanager.Create(new_bullet);
					p.sound_time += time;
					if (p.sound_time > 70.0f)
					{
						p.sound_time = 0;
						smanager.PlaySound("enemy_shot1");
					}
					p.fired++;
				}
				if (p.fired == p.bullet_number)
					p.active = 0;
			}
			// --------------------------------------  очередь из пуль в игрока ---------------------------------------------
			// Pattern Type  300 - 349
			else if (pat_type < 350)
			{
				// Очередь из пуль
				// bullet_number - количество пуль в выстреле
				// time1 - период выпуска
				p.time_working += time;
				p.sound_time += time;
				while ((p.bullet_time1 * p.fired < p.time_working) && (p.fired < p.bullet_number))
				{
					CreateEffect(p.bullet_effect, p.enemy_object->position, p.bullet_effect_index);
					float update_time = p.time_working - (p.bullet_time1 * p.fired);
					Bullet new_bullet = GetBulletTemplate(p, p.enemy_object->position);
					new_bullet.direction = get_direction_to_player(p.enemy_object->position);
					new_bullet.orientation = get_bullet_orientation(new_bullet.direction);
					new_bullet.Update(update_time);
					bmanager.Create(new_bullet);
					if (p.sound_time > 70.0f)
					{
						p.sound_time = 0;
						smanager.PlaySound("enemy_shot1");
					}
					p.fired++;
				}
				if (p.fired == p.bullet_number)
					p.active = 0;
			}
			// --------------------------------------  спираль из пуль ---------------------------------------------
			// Pattern Type  350 - 399
			else if (pat_type < 400)
			{
				// Спираль из пуль
				// bullet_number - количество пуль в выстреле
				// time1 - период выпуска
				// time2 - на протяжении какого времени стрелять
				// pattern_speed - угол между пулями
				// pattern_param1 - начальный угол
				// pattern_param2 - расстояние выпуска пули
				// pattern_param3 - угол сектора пуль
				p.time_working += time;
				while ((p.bullet_time1 * p.fired < p.time_working) && (p.time_working < p.bullet_time2))
				{
					float update_time = p.time_working - (p.bullet_time1 * p.fired);
					Bullet new_bullet = GetBulletTemplate(p, p.enemy_object->position);
					CL_Angle a(p.pattern_param1 + p.pattern_speed * (float)p.fired, cl_degrees);
					for (int i = 0; i < p.bullet_number; i++)
					{
						CL_Angle a1(((float)i + 0.5f) * (p.pattern_param3 / (float)p.bullet_number) - (p.pattern_param3 / 2), cl_degrees);
						new_bullet.direction = CL_Vec2f(cos((a + a1).to_radians()), sin((a + a1).to_radians()));
						new_bullet.orientation = a + a1;
						new_bullet.position = p.enemy_object->position + new_bullet.direction * p.pattern_param2;
						new_bullet.Update(update_time);
						bmanager.Create(new_bullet);
					}
					CreateEffect(p.bullet_effect, new_bullet.position, p.bullet_effect_index);
					p.sound_time += time;
					if (p.sound_time > 70.0f)
					{
						p.sound_time = 0;
						smanager.PlaySound("enemy_shot3");
					}
					p.fired++;
				}
				if (p.time_working > p.bullet_time2)
					p.active = 0;
				if (!p.enemy_object->alive) // прекращать стрельбу, если враг уничтожен
					p.active = 0;
			}
			// ---------------------------  сектор пуль перпендекулярно движению  -----------------------------------------
			// Pattern Type  400 - 449
			else if (pat_type < 450)
			{
				// Выстрел одновременно несколькими пулями
				// Меняется вид пули в записимости от типа
				// Сразу запускается эффект выстрела и сами пули
				// Положение не следует за врагом
				CreateEffect(p.bullet_effect, p.start_position, p.type % 20);
				// И звук выстрела
				smanager.PlaySound("enemy_shot1");

				// Перпендикулярное движение
				// Сектор из пуль
				CL_Angle a_start = get_bullet_orientation(p.enemy_object->move) - CL_Angle(90, cl_degrees);
				if (sin(a_start.to_radians()) < 0)
					a_start += CL_Angle(180, cl_degrees);
				for (int a = (int)p.pattern_param1; a >0; a--)
				{
					for (int t = 0; t < p.bullet_number; t++)
					{
						CL_Angle a1 = a_start + CL_Angle(a * p.pattern_param2, cl_degrees);
						CL_Angle a2 = a_start - CL_Angle(a * p.pattern_param2, cl_degrees);
						Bullet new_bullet = GetBulletTemplate(p, p.start_position);
						new_bullet.orientation = a1;
						new_bullet.direction = CL_Vec2<float>(cos(a1.to_radians()), sin(a1.to_radians()));
						new_bullet.speed = p.bullet_speed + ((float)t / 100);
						new_bullet.param_float1 = p.bullet_speed + (p.bullet_param1 - p.bullet_speed) * (float)(t + 1) / p.bullet_number;
						//new_bullet.param_float1 = p.bullet_param1 + ((float)t / 15);
						bmanager.Create(new_bullet);
						new_bullet.direction = CL_Vec2<float>(cos(a2.to_radians()), sin(a2.to_radians()));
						bmanager.Create(new_bullet);
					}
				}
				for (int t = 0; t < p.bullet_number; t++)
				{
					Bullet new_bullet = GetBulletTemplate(p, p.start_position);
					new_bullet.orientation = a_start;
					new_bullet.direction = CL_Vec2<float>(cos(a_start.to_radians()), sin(a_start.to_radians()));
					new_bullet.speed = p.bullet_speed + ((float)t / 100);
					new_bullet.param_float1 = p.bullet_speed + (p.bullet_param1 - p.bullet_speed) * (float)(t + 1) / p.bullet_number;
					bmanager.Create(new_bullet);
				}
				p.active = 0;
			}
			// --------------------------------------  очередь из пуль в сторону игрока ---------------------------------------------
			// Pattern Type  450 - 499
			else if (pat_type < 500)
			{
				// Очередь из пуль, первая выпускается в игрока, все остальные следуют за ней
				// bullet_number - количество пуль в выстреле
				// time1 - период выпуска
				if (!p.time_working)
				{
					// Начальные установки
					p.start_position = player.position; // все пули выстреливаются в одну точку
				}
				p.time_working += time;
				p.sound_time += time;
				while ((p.bullet_time1 * p.fired < p.time_working) && (p.fired < p.bullet_number))
				{
					CreateEffect(p.bullet_effect, p.enemy_object->position, p.bullet_effect_index);
					float update_time = p.time_working - (p.bullet_time1 * p.fired);
					Bullet new_bullet = GetBulletTemplate(p, p.enemy_object->position);
					new_bullet.direction = (p.start_position - p.enemy_object->position).normalize();
					new_bullet.orientation = get_bullet_orientation(new_bullet.direction);
					new_bullet.Update(update_time);
					bmanager.Create(new_bullet);
					if (p.sound_time > 70.0f)
					{
						p.sound_time = 0;
						smanager.PlaySound("enemy_shot1");
					}
					p.fired++;
				}
				if (p.fired == p.bullet_number)
					p.active = 0;
				if (!p.enemy_object->alive) // прекращать стрельбу, если враг уничтожен
					p.active = 0;
			}
			// --------------------------------------  очередь из пуль в определённом направлении ---------------------------------------------
			// Pattern Type  500 - 549
			else if (pat_type < 550)
			{
				// Очередь из пуль, первая выпускается в игрока, все остальные следуют за ней
				// bullet_number - количество пуль в выстреле
				// time1 - период выпуска
				// pattern_param1 - угол, под которым выпускается пуля
				p.time_working += time;
				p.sound_time += time;
				while ((p.bullet_time1 * p.fired < p.time_working) && (p.fired < p.bullet_number))
				{
					CreateEffect(p.bullet_effect, p.enemy_object->position, p.bullet_effect_index);
					float update_time = p.time_working - (p.bullet_time1 * p.fired);
					Bullet new_bullet = GetBulletTemplate(p, p.enemy_object->position);
					new_bullet.orientation = CL_Angle(p.pattern_param1, cl_degrees);
					new_bullet.direction = CL_Vec2f(cos(new_bullet.orientation.to_radians()), sin(new_bullet.orientation.to_radians()));
					new_bullet.Update(update_time);
					bmanager.Create(new_bullet);
					if (p.sound_time > 70.0f)
					{
						p.sound_time = 0;
						smanager.PlaySound("enemy_shot1");
					}
					p.fired++;
				}
				if (p.fired == p.bullet_number)
					p.active = 0;
				if (!p.enemy_object->alive) // прекращать стрельбу, если враг уничтожен
					p.active = 0;
			}
			// --------------------------------------  секторы пуль в игрока ---------------------------------------------
			// Pattern Type  550 - 599
			else if (pat_type < 600)
			{
				// Выстрел одновременно несколькими пулями
				// Сразу запускается эффект выстрела и сами пули
				// Положение следует за врагом
				// bullet_number - количество секторов
				// time1 - период выпуска
				// pattern_param1 - угол сектора
				// pattern_param2 - количество пуль в секторе
				// pattern_param3 - расстояние выпуска пули
				p.time_working += time;
				p.sound_time += time;
				while ((p.bullet_time1 * p.fired < p.time_working) && (p.fired < p.bullet_number))
				{
					// Выпустить очередной сектор
					float update_time = p.time_working - (p.bullet_time1 * p.fired);
					CL_Angle a_to_player = get_bullet_orientation(get_direction_to_player(p.enemy_object->position));
					CL_Angle sect(p.pattern_param1, cl_degrees);
					CL_Angle start_a = a_to_player - (sect/2.0f);
					CL_Angle div_a = sect / (p.pattern_param2 - 1);
					for (int b_in_s = 0; b_in_s < p.pattern_param2; b_in_s++)
					{
						CL_Angle b_a = start_a + div_a * b_in_s;
						Bullet new_bullet = GetBulletTemplate(p, p.enemy_object->position);
						new_bullet.orientation = b_a;
						new_bullet.direction = CL_Vec2f(cos(new_bullet.orientation.to_radians()), sin(new_bullet.orientation.to_radians()));
						new_bullet.position += new_bullet.direction * p.pattern_param3;
						CreateEffect(p.bullet_effect, new_bullet.position , p.bullet_effect_index);
						new_bullet.Update(update_time);
						bmanager.Create(new_bullet);
					}
					if (p.sound_time > 70.0f)
					{
						p.sound_time = 0;
						smanager.PlaySound("enemy_shot1");
					}
					p.fired++;
				}
				if (p.fired == p.bullet_number)
					p.active = 0;
				if (!p.enemy_object->alive) // прекращать стрельбу, если враг уничтожен
					p.active = 0;
			}
			// --------------------------------------  BulletML ---------------------------------------------
			// Pattern Type  600 - 649
			//else if (pat_type < 650)
			//{
			//	// Оболочка для BulletML
			//	// pbullet - объект, на котором запускается скрипт
			//	// speed - дефолтная скорость
			//	// behaviour - имя скрипта
			//	if (!p.time_working)
			//	{
			//		// Начальные установки
			//		p.pbullet = new Bullet;
			//		*p.pbullet = GetBulletTemplate(p, p.enemy_object->position);
			//		p.pbullet->position = p.enemy_object->position;
			//		p.pbullet->start_param_float1 = p.bullet_speed;
			//		p.bc = new BulletCommand(patterns.bmlparser, p.pbullet);
			//		p.bc->pattern_ = &p;
			//	}
			//	p.time_working += time;
			//	p.cur_turn++;
			//	p.bc->run();
			//	if (!p.enemy_object->alive) // прекращать стрельбу, если враг уничтожен
			//	{
			//		delete p.pbullet;
			//		p.active = 0;
			//	}
			//}
			// --------------------------------------  Лазоры ---------------------------------------------
			// -----------------------------------------------------------------------------------------------
			// -------------------------------------- Простой прямой лазор ---------------------------------------------
			// Pattern Type  10100 - 10199
			else if (pat_type < 10200)
			{
				// Прямой лазор
				// bullet_number - количество лучей
				// param1 - время действия лазора
				// pattern_param1 - угол выпуска
				// pattern_param2 - задержка перед выпуском
				if (!p.time_working)
				{
					// запустить эффект
				}
				p.time_working += time;
				if (p.time_working > p.pattern_param2)
				{
					// Запустить лазор
					float update_time = p.time_working - p.pattern_param2;
					Bullet new_bullet;
					new_bullet.prev_position = p.start_position;
					new_bullet.orientation = CL_Angle(p.pattern_param1, cl_degrees);
					new_bullet.position = new_bullet.prev_position + CL_Vec2f(800.0f * cos(new_bullet.orientation.to_radians()), 800.0f * sin(new_bullet.orientation.to_radians()));
					new_bullet.start_position = new_bullet.prev_position + CL_Vec2f(400.0f * cos(new_bullet.orientation.to_radians()), 400.0f * sin(new_bullet.orientation.to_radians()));
					new_bullet.behaviour = p.bullet_behaviour;
					new_bullet.appearance = renders.GetBulletRender("bullet_lazor1");
					new_bullet.appearance_index = p.type % 20;
					new_bullet.speed = p.bullet_speed;
					new_bullet.param_float1 = p.bullet_param1;
					new_bullet.start_param_float1 = p.bullet_start_param1;
					new_bullet.collision_size = 8;
					new_bullet.damage = 120;
					new_bullet.use_pattern = 0;
					new_bullet.is_lazor = 1;
					new_bullet.owner = 1;
					new_bullet.time_lived = 0;
					new_bullet.Update(update_time);
					bmanager.Create(new_bullet);
					smanager.PlaySound("enemy_shot1");
					p.active = 0;
				}
			}
			// ---------------------------------------------------------------------------
			// --------------------------------------  Паттерны спеллкарт ---------------------------------------------
			// --------------------------------------  Набор пуль Летти, уходящих вбок ---------------------------------------------
			// Pattern Type  20000 - 20099
			else if (pat_type < 20100)
			{
				// Выстрел несколькими пулями в игрока
				// Сразу запускается эффект выстрела и сами пули
				// Через определённое время пули останавливаются
				// И через некоторое время пули убираются, спавня другие
				// bullet_number - количество пуль
				// param1 - период выпуска пуль
				// start_param1 - время полёта пули
				// time1 - время ожидания
				// pattern_param1 - угол сектора
				// pattern_param2 - начальная скорость после ожидания
				// pattern_param3 - расстояние выпуска пуль
				if (!p.time_working)
				{
					p.bullet_time2 = get_bullet_orientation(get_direction_to_player(p.enemy_object->position)).to_radians();
				}
				p.time_working += time;
				while ((p.bullet_param1 * p.fired < p.time_working) && (p.fired < p.bullet_number))
				{
					// Выпустить пули, останавливающиеся по прошествии времени
					CL_Angle dr(p.bullet_time2, cl_radians);
					float bspeed1 = p.bullet_speed;
					float bspeed2 = p.pattern_param2;
					CL_Vec2f start_mov(cos(p.bullet_time2), sin(p.bullet_time2));
					Bullet new_bullet = GetBulletTemplate(p, p.enemy_object->position);
					new_bullet.orientation = CL_Angle(p.bullet_time2, cl_radians);
					new_bullet.direction = start_mov;
					new_bullet.position += new_bullet.direction * p.pattern_param3;
					new_bullet.speed = bspeed1;
					new_bullet.param_float1 = 0;
					new_bullet.start_param_float1 = p.bullet_start_param1 - p.fired * 20;
					// После паузы пуля будет уничтожена и заспаунит две новые пули
					new_bullet.use_pattern = 1;
					new_bullet.trigger = 0;
					new_bullet.pc.kill_bullet = 1;
					if (p.fired)
					{
						// Спауним по две пули
						new_bullet.pc.spawn_bullets = 2;
						CL_SharedPtr<float> cpr(new float[14]);
						new_bullet.pc.c_params = cpr;
						float* ctrlp = new_bullet.pc.c_params.get();
						ctrlp[0] = bspeed2 * 0.2f + (bspeed2 * 0.8f * (float)p.fired / (float)(p.bullet_number - 1));
						ctrlp[7] = ctrlp[0];
						ctrlp[1] = dr.to_degrees() + p.pattern_param1 * p.fired;
						ctrlp[8] = dr.to_degrees() - p.pattern_param1 * p.fired;
						cl_log_event("Debug", "Angle 1 = %1", ctrlp[1]);
						cl_log_event("Debug", "Angle 2 = %1", ctrlp[8]);
						ctrlp[2] = 0;
						ctrlp[3] = 0;
						ctrlp[4] = 0;
						ctrlp[5] = 0;
						ctrlp[6] = new_bullet.appearance_index;
						ctrlp[9] = 0;
						ctrlp[10] = 0;
						ctrlp[11] = 0;
						ctrlp[12] = 0;
						ctrlp[13] = new_bullet.appearance_index;
					}
					else
					{
						// Первая пуля
						new_bullet.pc.spawn_bullets = 1;
						CL_SharedPtr<float> cpr(new float[7]);
						new_bullet.pc.c_params = cpr;
						float* ctrlp = new_bullet.pc.c_params.get();
						ctrlp[0] = bspeed2 * 0.2f;
						ctrlp[1] = dr.to_degrees();
						ctrlp[2] = 0;
						ctrlp[3] = 0;
						ctrlp[4] = 0;
						ctrlp[5] = 0;
						ctrlp[6] = new_bullet.appearance_index;
					}
					// Запустим пулю
					bmanager.Create(new_bullet);
					p.active_bullets++;
					// И эффект
					CreateEffect(p.bullet_effect, new_bullet.position , p.bullet_effect_index);
					//for (int i = 0; i < p.bullet_number; i++)
					//{
					//	Bullet new_bullet = GetBulletTemplate(p, p.enemy_object->position);
					//	new_bullet.appearance = renders.GetBulletRender("bullet0");
					//	new_bullet.orientation = a_to_player;
					//	new_bullet.direction = start_mov;
					//	new_bullet.position += new_bullet.direction * p.pattern_param3;
					//	new_bullet.speed = bspeed1;
					//	new_bullet.param_float1 = bspeed2;
					//	new_bullet.start_param_float1 = p.bullet_start_param1;
					//	new_bullet.use_pattern = 1;
					//	// Направление движения пули после паузы идёт в start_position
					//	CL_Angle b_a = a_to_player + sect * (float)i;
					//	new_bullet.start_position = CL_Vec2f(cos(b_a.to_radians()), sin(b_a.to_radians()));
					//	bmanager.Create(new_bullet);
					//	p.active_bullets++;
					//	bspeed1 /= 1.4f;
					//	bspeed2 *= 1.4f;
					//}
					smanager.PlaySound("enemy_shot1");
					p.fired++;
				}
				if (p.time_working > p.bullet_time1)
					p.trigger = 1;
				if (!p.active_bullets)
					p.active = 0;
			}
			// -----------------------------------------------------------------------------------------------
			// Pattern Type  20100 - 20199
			else if (pat_type < 20200)
			{
				// Крутящийся паттерн, выпускающий пули из эффекта тумана
				// Сразу запускается эффект тумана
				// Постоянно идёт по кругу
				// Выпускаемые пули тоже крутятся
				// bullet_number - количество пуль
				// param1 - первичная скорость пули
				// start_param1 - время полёта пули
				// time1 - время ожидания до начала стрельбы
				// time2 - время действия паттерна
				// pattern_param1 - радиус точки выпуска
				// pattern_param2 - угловая скорость паттерна 
				// pattern_param3 - угловая скорость выпуска пуль 
				// pattern_param4 - начальный угол паттерна 
				// pattern_param5 - начальный угол выпуска пуль 
				// pattern_speed - период выпуска пуль
				if (!p.time_working)
				{
					p.work_param1 = p.bullet_time1; // время до выпуска следующей пули
					p.work_param2 = p.pattern_param4; // угол выпуска следующего эффекта
					p.work_param3 = 0; // Время до выпуска очередного эффекта
					p.work_param4 = 0; // Количество запущенных эффектов
				}
				else
				{
					p.work_param2 += p.pattern_param2 * time;
					if (p.fired)
					{
						p.pattern_param4 += p.pattern_param2 * time;
						p.pattern_param5 += p.pattern_param3 * time;
					}
				}
				p.time_working += time;
				p.work_param1 -= time;
				p.work_param3 -= time;
				if (p.work_param3 <=0 && p.time_working < p.bullet_time2)
				{
					// Запустить следующий эффект
					CL_Angle aeff(p.work_param2, cl_degrees);
					CL_Vec2f epos = p.start_position + CL_Vec2f(cos(aeff.to_radians()), sin(aeff.to_radians())) * p.pattern_param1;
					CreateEffect(p.bullet_effect, epos, p.bullet_effect_index);
					p.work_param3 += p.pattern_speed;
				}
				if (p.work_param1 <= 0)
				{
					// Выпускаем следующую пачку пуль
					float ba = CL_Angle(180.0f / float(p.bullet_number + 1), cl_degrees).to_radians();
					float astart = CL_Angle(p.pattern_param5 - 90.0f, cl_degrees).to_radians();
					for (int bn = 0; bn < p.bullet_number; bn++)
					{
						float ac = astart + float(bn + 1) * ba;
						CL_Angle bangle(p.pattern_param4, cl_degrees);
						CL_Vec2f bpos = p.start_position + CL_Vec2f(cos(bangle.to_radians()), sin(bangle.to_radians())) * p.pattern_param1;
						CL_Vec2f start_mov(cos(ac), sin(ac));
						Bullet new_bullet = GetBulletTemplate(p, bpos);
						new_bullet.orientation = CL_Angle(ac, cl_radians);
						new_bullet.direction = start_mov;
						new_bullet.speed = p.bullet_speed;
						new_bullet.param_float1 = p.bullet_param1;
						new_bullet.start_param_float1 = p.bullet_start_param1;
						bmanager.Create(new_bullet);
					}
					smanager.PlaySound("enemy_shot1");
					p.fired++;
					p.work_param1 += p.pattern_speed;
				}

				if (p.time_working > p.bullet_time1 + p.bullet_time2)
					p.active = 0;
			}
			// -----------------------------------------------------------------------------------------------
			else if (pat_type < 20300)
			{
				// Кольца пуль из удалённой точки
				// bullet_number - количество колец
				// param1 - первичная скорость пули
				// time1 - период выпуска пуль
				// pattern_param1 - угол смещения кольца
				// pattern_param2 - количество пуль в кольце
				// pattern_param3 - угол начала выпуска
				// pattern_param4 - расстояние выпуска
				if (!p.time_working)
				{
					// Начальные установки
					CL_Angle a(p.pattern_param3, cl_degrees);
					p.start_position = p.enemy_object->position + CL_Vec2f(cos(a.to_radians()), sin(a.to_radians())) * p.pattern_param4;
				}
				p.time_working += time;
				p.sound_time += time;
				while ((p.bullet_time1 * p.fired < p.time_working) && (p.fired < p.bullet_number))
				{
					CreateEffect(p.bullet_effect, p.start_position, p.bullet_effect_index);
					float update_time = p.time_working - (p.bullet_time1 * p.fired);
					// Выпускаем кольцо
					float astart = CL_Angle(p.pattern_param1, cl_degrees).to_radians();
					float astep = 2.0f * 3.14159265358979f / (float)p.pattern_param2;
					for (int bn = 0; bn < p.pattern_param2; bn++)
					{
						Bullet new_bullet = GetBulletTemplate(p, p.start_position);
						new_bullet.orientation = CL_Angle(astart + astep*bn, cl_radians);
						new_bullet.CalculateDirection();
						new_bullet.speed = p.bullet_speed;
						new_bullet.param_float1 = p.bullet_param1;
						new_bullet.start_param_float1 = p.bullet_start_param1;
						new_bullet.Update(update_time);
						bmanager.Create(new_bullet);
					}
					if (p.sound_time > 70.0f)
					{
						p.sound_time = 0;
						smanager.PlaySound("enemy_shot2");
					}
					p.fired++;
				}
				if (p.fired == p.bullet_number)
					p.active = 0;
			}
			// -----------------------------------------------------------------------------------------------
		}
		// =====================================================
		// Отсюда идут паттерны игрока
		else
		{
			// -------------------------- простая шмальба -------------------------------------------------
			if (p.type == 100000001)
			{
				// Обычный выстрел Аи
				// Поток быстрых пуль веером
				// Положение следует за игроком
				// В режиме фокуса - другие широкие пули только вперёд
				if (player.dead)
				{
					p.active = 0;
					break;
				}
				p.time_working += time;
				player_shot_sound_time += time;
				// Запускаем пули каждые shot1_interval мс
				while ((player.shot1_interval * p.fired < p.time_working) && (p.fired < player.shot1_bullets))
				{
					Bullet new_bullet = GetBulletTemplate(p, player.position);

					// Позиция и Направление
					p.start_position = player.position;
					if (!player.focused)
						p.start_position.y = p.start_position.y + 20.0f;
					float update_time = p.time_working - (player.shot1_interval * p.fired);
					//float shooting_range = (float)player.visible_size.height / 2 - 2;
					float shooting_range = 2.0f;

					CL_Angle angle;

					// Power level 1 - 128
					if (player.focused)
					{
						if (player.power > 127)
						{
							new_bullet.appearance = renders.GetBulletRender("player_bullet_focus5");
							new_bullet.collision_size = 36;
							new_bullet.damage = player.shot4_damage * 2.2f;
							new_bullet.speed = player.shot4_speed;

							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x, p.start_position.y + shooting_range * new_bullet.direction.y);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}
						else if (player.power > 95)
						{
							new_bullet.appearance = renders.GetBulletRender("player_bullet_focus4");
							new_bullet.collision_size = 31;
							new_bullet.damage = player.shot4_damage * 1.9f;
							new_bullet.speed = player.shot4_speed;

							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x, p.start_position.y + shooting_range * new_bullet.direction.y);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}
						else if (player.power > 63)
						{
							new_bullet.appearance = renders.GetBulletRender("player_bullet_focus3");
							new_bullet.collision_size = 29;
							new_bullet.damage = player.shot4_damage * 1.6f;
							new_bullet.speed = player.shot4_speed;

							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x, p.start_position.y + shooting_range * new_bullet.direction.y);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}
						else if (player.power > 31)
						{
							new_bullet.appearance = renders.GetBulletRender("player_bullet_focus2");
							new_bullet.collision_size = 26;
							new_bullet.damage = player.shot4_damage *1.3f;
							new_bullet.speed = player.shot4_speed;

							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x, p.start_position.y + shooting_range * new_bullet.direction.y);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}
						else
						{
							new_bullet = GetBulletTemplate(p, player.position);

							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x, p.start_position.y + shooting_range * new_bullet.direction.y);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}
					}
					else
					{
						new_bullet.appearance = renders.GetBulletRender("player_bullet2");
						new_bullet.collision_size = 6;
						new_bullet.damage = player.shot3_damage;
						new_bullet.speed = player.shot3_speed;
						
						if (player.power > 127)
						{
							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x , p.start_position.y + shooting_range * new_bullet.direction.y - 5);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);

							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x - 16, p.start_position.y + shooting_range * new_bullet.direction.y + 7);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);

							new_bullet.position = CL_Vec2f(p.start_position.x + 16, p.start_position.y + shooting_range * new_bullet.direction.y + 7);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}
						if (player.power > 95)
						{
							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x - 49, p.start_position.y + shooting_range * new_bullet.direction.y + 30);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);

							new_bullet.position = CL_Vec2f(p.start_position.x + 49, p.start_position.y + shooting_range * new_bullet.direction.y + 30);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}
						if (player.power > 63)
						{
							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x - 35, p.start_position.y + shooting_range * new_bullet.direction.y + 20);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);

							new_bullet.position = CL_Vec2f(p.start_position.x + 35, p.start_position.y + shooting_range * new_bullet.direction.y + 20);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}

						new_bullet = GetBulletTemplate(p, player.position);

						if (player.power > 31)
						{
							new_bullet.direction = CL_Vec2f(0, -1);
							new_bullet.position = CL_Vec2f(p.start_position.x - 20, p.start_position.y + shooting_range * new_bullet.direction.y + 7);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);

							new_bullet.position = CL_Vec2f(p.start_position.x + 20, p.start_position.y + shooting_range * new_bullet.direction.y + 7);
							new_bullet.Update(update_time);
							bmanager.Create(new_bullet);
						}
						new_bullet.direction = CL_Vec2f(0, -1);
						new_bullet.position = CL_Vec2f(p.start_position.x - 9, p.start_position.y + shooting_range * new_bullet.direction.y);
						new_bullet.Update(update_time);
						bmanager.Create(new_bullet);
						new_bullet.position = CL_Vec2f(p.start_position.x + 9, p.start_position.y + shooting_range * new_bullet.direction.y);
						new_bullet.Update(update_time);
						bmanager.Create(new_bullet);
					}

					p.fired++;
					if (player_shot_sound_time > 70)
					{
						player_shot_sound_time = 0;
						smanager.PlaySound("player_shot");
					}
				}
				if (p.fired == player.shot1_bullets)
					p.active = 0;
			}
			// ---------------------------------------------------------------------------
			// -------------------------- дополнительные пули к обычным выстрелам --------------------------------------------
			if (p.type == 100000002)
			{
				// Дополнительные выстрелы большими пулями
				// Более медленные большие пули, с большим дамагом
				// Сначала запускается эффект выстрела, потом идёт поток пуль
				// Положение следует за игроком, 
				// выстрел запускается по ближайшему противнику, но пули не самонаводящиеся
				if (p.time_working == 0)
				{
					// начальная установка - определить куда стрелять
					p.start_position = emanager.GetClosestEnemyPosition();
				}
				p.time_working += time;
				unsigned int to_fire = (player.power - player.shot2_startpower) / player.shot2_steppower + 1;
				// Запускаем пули каждые shot2_interval мс
				while ((player.shot2_interval * p.fired < p.time_working) && (p.fired < to_fire))
				{
					Bullet new_bullet = GetBulletTemplate(p, player.position);

					// Позиция и Направление
					float update_time = p.time_working - (player.shot2_interval * p.fired);
					float shooting_range = (float)player.visible_size.height / 2;

					if (p.start_position == CL_Vec2<float>(0,0))
						new_bullet.direction = CL_Vec2<float>(0,-1);
					else
						new_bullet.direction = (p.start_position - player.position).normalize();
					new_bullet.position.y -= shooting_range;
					new_bullet.Update(update_time);
					bmanager.Create(new_bullet);

					new_bullet.direction.rotate(CL_Vec2<float>(0,0), CL_Angle(-5, cl_degrees));
					new_bullet.position = player.position;
					new_bullet.position.x -= shooting_range;
					new_bullet.Update(update_time);
					bmanager.Create(new_bullet);

					new_bullet.direction.rotate(CL_Vec2<float>(0,0), CL_Angle(10, cl_degrees));
					new_bullet.position = player.position;
					new_bullet.position.x += shooting_range;
					new_bullet.Update(update_time);
					bmanager.Create(new_bullet);

					p.fired++;
				}
				if (p.fired == to_fire)
					p.active = 0;
			}
			// ---------------------------------------------------------------------------
			// -------------------------- Автопули спеллкарты "Перекрестье" --------------------------------------------
			if (p.type == 100000005)
			{
				// Кольцо пуль, идущих по дуге поочерёдно с заворотом то влево, то вправо
				// Пересекаются друг с другом за счёт этого
				if (!p.trigger)
				{
					// Запускаем кольцо пуль
					bool a_trig = true;
					for ( int a = 0; a < 360; a+=(360 / player.shot3_bullets))
					{
						Bullet new_bullet = GetBulletTemplate(p, player.position);
						new_bullet.pattern = _patterns[t].get();
						new_bullet.use_pattern = 1;

						// Позиция и Направление
						p.start_position = player.position;
						CL_Angle angle((float)a, cl_degrees);
						new_bullet.direction = CL_Vec2<float>(cos(angle.to_radians()), sin(angle.to_radians()));
						new_bullet.orientation = angle;
						new_bullet.start_param_float1 = (float)a;
						if (a_trig)
							new_bullet.param_float1 = player.shot3_angle_speed;
						else
							new_bullet.param_float1 = -player.shot3_angle_speed;
						a_trig = !a_trig;
						// эффект выстрела
						CreateEffect("shot1", p.start_position + new_bullet.direction * 10, 1);
						// и создаём
						bmanager.Create(new_bullet);
						p.active_bullets++;
					}
					// звук выстрела
					smanager.PlaySound_VolumeControl("enemy_shot3", 0.5f);
					p.trigger = 1;
				}
				else
				{
					// Паттерн можно убирать, как только все пули будут уничтожены
					if (!p.active_bullets)
						p.active = 0;
				}
			}
			// ---------------------------------------------------------------------------
			// -------------------------- Наводимые пули спеллкарты "Перекрестье -------------------------------------------------
			if (p.type == 100000006)
			{
				// Идут по дуге потом выпрямляясь по касательной
				// Сплошной поток пуль во много рядов
				// Положение не следует за игроком
				if (p.time_working == 0)
					p.start_position = player.position;
				p.time_working += time;
				player_shot_sound_time += time;
				// Запускаем пули каждые shot4_interval мс
				while (player.spellcard_activated && (player.shot4_interval * p.fired < p.time_working) && (p.fired < player.shot4_bullets))
				{
					// Позиция и Направление
					float update_time = p.time_working - (player.shot4_interval * p.fired);
					float shooting_range = 12.0f;
					CL_Angle angle(90, cl_degrees);
					CL_Angle a;
					for (int i = 0; i < 5; i++) // По сколько потоков в каждую сторону
					{
						CL_Angle da((float)i*10, cl_degrees);

						Bullet new_bullet = GetBulletTemplate(p, player.position);
						new_bullet.pattern = _patterns[t].get();
						new_bullet.use_pattern = 1;
						
						a = angle + player.focus_angle - da;

						new_bullet.start_param_float1 = a.to_degrees();
						new_bullet.start_position = player.position;
						new_bullet.orientation = a;
						new_bullet.param_float1 = player.shot4_angle_speed;
						new_bullet.direction = CL_Vec2<float>(cos(a.to_radians()), sin(a.to_radians()));
						new_bullet.position = CL_Vec2<float>(player.position.x + shooting_range * new_bullet.direction.x, player.position.y + shooting_range * new_bullet.direction.y);
						new_bullet.Update(update_time);
						CreateEffect("shot1", player.position + new_bullet.direction * 10, 4);
						bmanager.Create(new_bullet);
						p.active_bullets++;

						new_bullet = GetBulletTemplate(p, player.position);
						new_bullet.pattern = _patterns[t].get();
						new_bullet.use_pattern = 1;
						
						a = angle + player.focus_angle + da;

						new_bullet.start_param_float1 = a.to_degrees();
						new_bullet.start_position = player.position;
						new_bullet.orientation = a;
						new_bullet.param_float1 = -player.shot4_angle_speed;
						new_bullet.direction = CL_Vec2<float>(cos(a.to_radians()), sin(a.to_radians()));
						new_bullet.position = CL_Vec2<float>(player.position.x + shooting_range * new_bullet.direction.x, player.position.y + shooting_range * new_bullet.direction.y);
						new_bullet.Update(update_time);
						CreateEffect("shot1", player.position + new_bullet.direction * 10, 4);
						bmanager.Create(new_bullet);
						p.active_bullets++;
					}

					p.fired++;
					if (player_shot_sound_time > 70)
					{
						player_shot_sound_time = 0;
						smanager.PlaySound_VolumeControl("enemy_shot1", 0.5f);
					}
				}
				if (((p.fired == player.shot4_bullets) || !player.spellcard_activated) && !p.active_bullets)
					p.active = 0;
			}
			// ---------------------------------------------------------------------------
		}
	}
	// Удаляем отработавшие паттерны
	t = 0;
	while (t < _patterns.size())
		if (_patterns[t]->active)
			t++;
		else
		{
			//cl_log_event("Debug", "Deleting pattern %1, patterns total: %2", _patterns[t]->id, _patterns.size()-1);
			_patterns.erase(_patterns.begin() + t);
		}
}

// Сконструировать пулю для паттерна
// Первые три цифры - вид пули
// Остальные - поведение и другие параметры
Bullet PatternManager::GetBulletTemplate(Pattern &pat, CL_Vec2<float> pos)
{
	int b_visuals = pat.type % 1000; // Параметры отображения пули
	int b_index = b_visuals % 20; // Индекс цвета пули
	int b_type = b_visuals / 20;   // Тип пули
	int b_ptn = pat.type / 1000; // Тип паттерна

	Bullet t_bullet;
	// Определить внешний вид и размер из темплейта
	CL_String bname = CL_String("bullet") + CL_StringHelp::int_to_text(b_type);
	t_bullet = bullet_templates[bname.c_str()];
	// Взять дефолтные настройки из паттерна
	t_bullet.behaviour = pat.bullet_behaviour;
	t_bullet.speed = pat.bullet_speed;
	t_bullet.param_float1 = pat.bullet_param1;
	t_bullet.start_param_float1 = pat.bullet_start_param1;
	// ===============================
	t_bullet.position = pos;
	t_bullet.prev_position = pos;
	t_bullet.use_pattern = 0;
	t_bullet.time_lived = 0;
	t_bullet.trigger = 0;
	t_bullet.pattern = &pat;

	if (pat.type > 100000000)
	{
		// пули игрока
		t_bullet.owner = 0;
		if (pat.type == 100000001)
		{
			if (player.focused)
			{
				t_bullet.appearance = renders.GetBulletRender("player_bullet_focus1");
				t_bullet.collision_size = 24;
				t_bullet.damage = player.shot2_damage;
				t_bullet.speed = player.shot2_speed;
			}
			else
			{
				t_bullet.appearance = renders.GetBulletRender("player_bullet1");
				t_bullet.collision_size = 8;
				t_bullet.damage = player.shot1_damage;
				t_bullet.speed = player.shot1_speed;
			}
			t_bullet.appearance_index = 6;
			t_bullet.behaviour = GetBulletBehaviour("bullet_simple");
		}
		if (pat.type == 100000002)
		{
			t_bullet.appearance = renders.GetBulletRender("player_bullet2");
			t_bullet.appearance_index = 3;
			t_bullet.behaviour = GetBulletBehaviour("bullet_simple");
			t_bullet.collision_size = 30;
			t_bullet.damage = player.shot2_damage;
			t_bullet.speed = player.shot2_speed;
		}
		if (pat.type == 100000005)
		{
			t_bullet.appearance = renders.GetBulletRender("bullet_pike1");
			t_bullet.appearance_index = 1;
			t_bullet.behaviour = GetBulletBehaviour("bullet_arc_polar_player1");
			t_bullet.collision_size = 6;
			t_bullet.damage = player.shot3_damage;
			t_bullet.speed = player.shot3_speed;
		}
		if (pat.type == 100000006)
		{
			t_bullet.appearance = renders.GetBulletRender("player_bullet1");
			t_bullet.appearance_index = 6;
			t_bullet.behaviour = GetBulletBehaviour("bullet_player_1");
			t_bullet.collision_size = 6;
			t_bullet.damage = player.shot4_damage;
			t_bullet.speed = player.shot4_speed;
		}
	}
	else
	{
		// пули противников
		t_bullet.owner = 1;
		t_bullet.appearance_index = b_index;
	}
	return t_bullet;
}

// Получить темплейт из списка
Bullet PatternManager::GetBulletTemplateByName(CL_String bname)
{
	Bullet t_bullet;
	if (bullet_templates.find(bname.c_str()) != bullet_templates.end())
		t_bullet = bullet_templates[bname.c_str()];
	return t_bullet;
}

void PatternManager::CreateEffect(CL_String name, CL_Vec2<float> pos, int index)
{
	Effect shot_effect;
	if (name == "shot1")
	{
		shot_effect.position = pos;
		// цвет в зависимости от типа
		shot_effect.appearance_index = index;
		shot_effect.appearance = renders.GetEffectRender("enemy_shot100");
		shot_effect.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		effects.Create(shot_effect);
	}
	if (name == "shot2")
	{
		shot_effect.position = pos;
		// цвет в зависимости от типа
		shot_effect.appearance_index = index;
		shot_effect.appearance = renders.GetEffectRender("enemy_shot200");
		shot_effect.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		effects.Create(shot_effect);
	}
	if (name == "shot5")
	{
		shot_effect.position = pos;
		// цвет в зависимости от типа
		shot_effect.appearance_index = index;
		shot_effect.appearance = renders.GetEffectRender("enemy_shot500");
		shot_effect.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		effects.Create(shot_effect);
	}
	if (name == "quickshot1")
	{
		shot_effect.position = pos;
		// цвет в зависимости от типа
		shot_effect.appearance_index = index;
		shot_effect.appearance = renders.GetEffectRender("enemy_shot_other100");
		shot_effect.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		effects.Create(shot_effect);
	}
	if (name == "quickshot2")
	{
		shot_effect.position = pos;
		// цвет в зависимости от типа
		shot_effect.appearance_index = index;
		shot_effect.appearance = renders.GetEffectRender("enemy_shot_other200");
		shot_effect.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		effects.Create(shot_effect);
	}
	if (name == "quickshot5")
	{
		shot_effect.position = pos;
		// цвет в зависимости от типа
		shot_effect.appearance_index = index;
		shot_effect.appearance = renders.GetEffectRender("enemy_shot_other500");
		shot_effect.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		effects.Create(shot_effect);
	}
	if (name == "letty_clouds")
	{
		shot_effect.position = pos;
		// цвет в зависимости от типа
		shot_effect.appearance_index = index;
		shot_effect.appearance = renders.GetEffectRender("letty_cloud");
		shot_effect.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		effects.Create(shot_effect);
	}
}

CL_Vec2<float> PatternManager::get_direction_to_player(CL_Vec2<float> from_where)
{
	return (player.position - from_where).normalize();
}

CL_Angle PatternManager::get_bullet_orientation(CL_Vec2f d)
{
	CL_Angle a(d.angle(CL_Vec2f(1,0)));
	if (d.y < 0)
		a = CL_Angle(360, cl_degrees) - a;
	//else
	//	a = CL_Angle(180, cl_degrees) + a;
	return a;
}

// Алгоритм плавного движения из одной точки в другую с разгоном и торможением
CL_Vec2f PatternManager::get_move_position1(CL_Vec2f start_pos, CL_Vec2f end_pos, float v, float a, float t)
{
	float len = start_pos.distance(end_pos);
	float t1 = v / a;
	float l1 = a * t1 * t1 / 2;
	float l2 = len - l1;
	float t2 = t1 + (len - l1*2)/v;
	float v1 = v;

	if (l1 > len / 2)
	{
		l1 = l2 = len / 2;
		t1 = t2 = sqrt(2 * l1 / a);
		v1 = t1 * a;
	}

	float l = 0;
	if (t < t1)
	{
		// Разгон
		l = a * t * t / 2;
	}
	else if (t < t2)
	{
		// прямой участок
		l = l1 + v1 * (t - t1);
	}
	else
	{
		// Торможение
		l = l2 + v1 * (t - t2) - a * (t - t2) * (t - t2) / 2;
	}
	return start_pos + (end_pos - start_pos) * (l / len);
}

// Другой алгоритм плавного движения из одной точки в другую с разгоном и торможением
CL_Vec2f PatternManager::get_move_position2(CL_Vec2f start_pos, CL_Vec2f end_pos, float time_to_move, float slowdown, float t)
{
	float len = start_pos.distance(end_pos);
	float t1 = time_to_move * slowdown;
	float t2 = time_to_move - t1;
	float a = len / (t1 * t2);
	float v = t1 * a;
	float l1 = a * t1 * t1 / 2;
	float l2 = len - l1;

	float l = 0;
	if (t < t1)
	{
		// Разгон
		l = a * t * t / 2;
	}
	else if (t < t2)
	{
		// прямой участок
		l = l1 + v * (t - t1);
	}
	else
	{
		// Торможение
		l = l2 + v * (t - t2) - a * (t - t2) * (t - t2) / 2;
	}
	return start_pos + (end_pos - start_pos) * (l / len);
}

// ================================================================

// Системы поведения пуль

// Простая летящая по прямой пуля
Behaviour_Simple::Behaviour_Simple(CL_Rectf clipping_rect)
{
	visible_rect = clipping_rect;
}

bool Behaviour_Simple::Update(Bullet *p_bullet, float time)
{
	p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	return InsideScreen(p_bullet->position);
}

// Летящая по прямой пуля, через определённый промежуток времени меняющая скорость
Behaviour_Simple2::Behaviour_Simple2(CL_Rectf clipping_rect, float d_time)
{
	visible_rect = clipping_rect;
	dtime = d_time;
}

bool Behaviour_Simple2::Update(Bullet *p_bullet, float time)
{
	p_bullet->time_lived += time;
	if (p_bullet->time_lived > dtime)
		p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	else
	{
		float tspeed = p_bullet->speed + (p_bullet->param_float1 - p_bullet->speed) * ((dtime - p_bullet->time_lived) / dtime);
		p_bullet->position += p_bullet->direction * tspeed * time;
	}
	return InsideScreen(p_bullet->position);
}

// Пуля с ускорением, как положительным, так и отрицательным - ускорение прибавляется скорости
Behaviour_Simple3::Behaviour_Simple3(CL_Rectf clipping_rect, float d_accel, float t_speed)
{
	visible_rect = clipping_rect;
	daccel = d_accel;
	topspeed = t_speed;
}

bool Behaviour_Simple3::Update(Bullet *p_bullet, float time)
{
	p_bullet->time_lived += time;
	float dspeed = daccel * time;
	p_bullet->speed += dspeed;
	if (daccel > 0)
	{
		if (p_bullet->speed > topspeed)
			p_bullet->speed = topspeed;
	}
	else
	{
		if (p_bullet->speed < topspeed)
			p_bullet->speed = topspeed;
	}
	p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	return InsideScreen(p_bullet->position);
}

// Летящая по прямой пуля, через определённый промежуток времени меняющая скорость
Behaviour_Timed::Behaviour_Timed(CL_Rectf clipping_rect)
{
	visible_rect = clipping_rect;
}

bool Behaviour_Timed::Update(Bullet *p_bullet, float time)
{
	p_bullet->time_lived += time;
	if (p_bullet->time_lived > p_bullet->start_param_float1)
		p_bullet->speed = p_bullet->param_float1;
	p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	return InsideScreen(p_bullet->position);
}

// Летящая по прямой пуля, через определённое расстояние останавливающаяся
Behaviour_Stopping::Behaviour_Stopping(CL_Rectf clipping_rect)
{
	visible_rect = clipping_rect;
}

bool Behaviour_Stopping::Update(Bullet *p_bullet, float time)
{
	p_bullet->time_lived += time;
	if (p_bullet->speed)
	{
		float time1;
		if ((p_bullet->param_float1 + p_bullet->speed * time) > p_bullet->start_param_float1)
			time1 = (p_bullet->start_param_float1 - p_bullet->param_float1) / p_bullet->speed;
		else
			time1 = time;
		p_bullet->param_float1 += p_bullet->speed * time1;
		p_bullet->position += p_bullet->direction * p_bullet->speed * time1;
		if (time != time1)
			p_bullet->speed = 0;
	}
	return InsideScreen(p_bullet->position);
}

// Летящая по прямой пуля, через определённое расстояние меняющая угол
Behaviour_Turning::Behaviour_Turning(CL_Rectf clipping_rect)
{
	visible_rect = clipping_rect;
}

bool Behaviour_Turning::Update(Bullet *p_bullet, float time)
{
	p_bullet->time_lived += time;
	if (!p_bullet->trigger)
	{
		if (p_bullet->time_lived > p_bullet->param_float1)
		{
			p_bullet->position += p_bullet->direction * p_bullet->speed * (time - (p_bullet->time_lived - p_bullet->param_float1));
			p_bullet->orientation += CL_Angle(p_bullet->start_param_float1, cl_degrees);
			p_bullet->CalculateDirection();
			p_bullet->trigger = 1;
		}
		else
			p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	}
	else
	{
		p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	}
	return InsideScreen(p_bullet->position);
}


// Пуля с гиперболическим ускорением, как на увеличение, так на уменьшение скорости - ускорение умножается на скорость

// Пуля с векторным ускорением

// Пуля со сменой скорости и ускорения после определённого пройденного пути

// Пуля с угловым движением, идёт по спирали/арке
Behaviour_Arc::Behaviour_Arc(CL_Rectf clipping_rect, float d_time, float d_speed)
{
	visible_rect = clipping_rect;
	dtime = d_time;
	dspeed = d_speed;
}

bool Behaviour_Arc::Update(Bullet *p_bullet, float time)
{
	p_bullet->time_lived += time;
	if (p_bullet->time_lived > dtime)
	{
		p_bullet->orientation += CL_Angle(p_bullet->param_float1, cl_degrees);
		p_bullet->direction = CL_Vec2<float>(cos(p_bullet->orientation.to_radians()), sin(p_bullet->orientation.to_radians()));
		p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	}
	else
	{
		float aspeed = p_bullet->param_float1 + (p_bullet->param_float1 * dspeed - p_bullet->param_float1) * ((dtime - p_bullet->time_lived) / dtime);
		float tspeed = p_bullet->speed + (p_bullet->speed * dspeed - p_bullet->speed) * ((dtime - p_bullet->time_lived) / dtime);
		p_bullet->orientation += CL_Angle(aspeed, cl_degrees);
		p_bullet->direction = CL_Vec2<float>(cos(p_bullet->orientation.to_radians()), sin(p_bullet->orientation.to_radians()));
		p_bullet->position += p_bullet->direction * tspeed * time;
	}
	return InsideScreen(p_bullet->position);
}

// Пуля с угловым движением, выпрямляющаяся в касательную через промежуток времени
Behaviour_Arc_Line::Behaviour_Arc_Line(CL_Rectf clipping_rect, float d_time, float d_speed)
{
	visible_rect = clipping_rect;
	dtime = d_time;
	dspeed = d_speed;
}

bool Behaviour_Arc_Line::Update(Bullet *p_bullet, float time)
{
	p_bullet->time_lived += time;
	if (p_bullet->time_lived > dtime)
	{
		p_bullet->position += p_bullet->direction * p_bullet->speed * dspeed * time;
	}
	else
	{
		p_bullet->orientation += CL_Angle(p_bullet->param_float1, cl_degrees);
		p_bullet->direction = CL_Vec2<float>(cos(p_bullet->orientation.to_radians()), sin(p_bullet->orientation.to_radians()));
		p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	}
	return InsideScreen(p_bullet->position);
}

// Пуля с угловым движением, идёт по спирали в полярных координатах
Behaviour_Arc_Polar::Behaviour_Arc_Polar(CL_Rectf clipping_rect, float d_time, float d_speed)
{
	visible_rect = clipping_rect;
	dtime = d_time;
	dspeed = d_speed;
}

bool Behaviour_Arc_Polar::Update(Bullet *p_bullet, float time)
{
	// Здесь линейная и угловая скорости
	// p_bullet->speed			- линейная
	// p_bullet->param_float1	- угловая
	// Коррекции скорости при удалении от центра нет
	// Центр берётся из объекта паттерна
	p_bullet->time_lived += time;
	if (p_bullet->time_lived > dtime)
	{
		float radius = (dtime * p_bullet->speed + (p_bullet->time_lived - dtime) * p_bullet->speed * dspeed) / 1000.0f;
		float a = p_bullet->start_param_float1 + p_bullet->time_lived * p_bullet->param_float1 / 1000.0f;
		p_bullet->position = p_bullet->pattern->start_position + CL_Vec2<float>(radius * cos(CL_Angle(a, cl_degrees).to_radians()), radius * sin(CL_Angle(a, cl_degrees).to_radians()));
		p_bullet->direction = (p_bullet->position - p_bullet->prev_position).normalize();
		//p_bullet->orientation = CL_Angle(a + 90.0f, cl_degrees); // тут рассчитывать касательную к спирали
	}
	else
	{
		float radius = p_bullet->time_lived * p_bullet->speed / 1000.0f;
		float a = p_bullet->start_param_float1 + p_bullet->time_lived * p_bullet->param_float1 / 1000.0f;
		p_bullet->position = p_bullet->pattern->start_position + CL_Vec2<float>(radius * cos(CL_Angle(a, cl_degrees).to_radians()), radius * sin(CL_Angle(a, cl_degrees).to_radians()));
		p_bullet->direction = (p_bullet->position - p_bullet->prev_position).normalize();
		//p_bullet->orientation = CL_Angle(a + 90.0f, cl_degrees); // тут рассчитывать касательную к спирали
	}
	return InsideScreen(p_bullet->position);
}

// ====  Специфичные траектории пуль спеллкарт ====

// Спеллкарта "Перекрестье" игрока, наводимые пули, с движением по окружности
Behaviour_Player_Bullet1::Behaviour_Player_Bullet1(CL_Rectf clipping_rect, float _radius, float d_angle, float d_speed)
{
	visible_rect = clipping_rect;
	dangle = d_angle;
	dspeed = d_speed;
	radius = _radius;
}

bool Behaviour_Player_Bullet1::Update(Bullet *p_bullet, float time)
{
	// Здесь линейная и угловая скорости
	// p_bullet->speed			- линейная
	// p_bullet->param_float1	- угловая
	// Коррекция скорости при переходе на окружность
	// Центр хранится в start_position
	// p_bullet->active работает триггером
	p_bullet->time_lived += time;
	if (p_bullet->active == 1)
	{
		// начальное движение  по спирали
		float r = p_bullet->time_lived * p_bullet->speed / 1000.0f;
		if (r > radius)
		{
			r = radius;
			p_bullet->active = 2;
		}
		float a = p_bullet->time_lived * p_bullet->param_float1 / 1000.0f;
		CL_Angle an(a + p_bullet->start_param_float1, cl_degrees);
		p_bullet->position = p_bullet->start_position + CL_Vec2<float>(r * cos(an.to_radians()), r * sin(an.to_radians()));
		p_bullet->direction = (p_bullet->position - p_bullet->prev_position).normalize();
	}
	else if (p_bullet->active == 2)
	{
		// переход на окружность
		float t1 = (radius / p_bullet->speed) * 1000.0f;
		float a1 = t1 * p_bullet->param_float1 / 1000.0f + (p_bullet->time_lived - t1) * p_bullet->param_float1 / 1000.0f;
		float a2 = (a1<0?-1:1) * (p_bullet->time_lived - t1) * (p_bullet->speed *360.0f / (radius * 3.1415926f)) / 1000.0f; // добавление радиальной скорости к угловой
		if (abs(a1 + a2) > dangle)
		{
			CL_Angle an((a1<0?-1:1)*dangle + p_bullet->start_param_float1, cl_degrees);
			p_bullet->position = p_bullet->start_position + CL_Vec2<float>(radius * cos(an.to_radians()), radius * sin(an.to_radians()));
			p_bullet->orientation = an + CL_Angle((a1<0?-1:1) * 90.0f, cl_degrees); // касательная
			p_bullet->direction = CL_Vec2<float>(cos(p_bullet->orientation.to_radians()), sin(p_bullet->orientation.to_radians()));
			//float dtime = p_bullet->time_lived - (dangle - t1 * p_bullet->param_float1 / 1000.0f) / ((p_bullet->param_float1 / 1000.0f) + ((p_bullet->speed *360.0f / (radius * 3.1415926f)) / 1000.0f));
			//p_bullet->position += p_bullet->direction * p_bullet->speed * dspeed * dtime / 1000.0f;
			p_bullet->active = 3;
		}
		else
		{
			CL_Angle an(a1 + a2 + p_bullet->start_param_float1, cl_degrees);
			p_bullet->position = p_bullet->start_position + CL_Vec2<float>(radius * cos(an.to_radians()), radius * sin(an.to_radians()));
			p_bullet->direction = (p_bullet->position - p_bullet->prev_position).normalize();
		}
	}
	else
	{
		// финальный выход на прямую
		p_bullet->position += p_bullet->direction * p_bullet->speed * dspeed * time / 1000.0f;
	}
	return InsideScreen(p_bullet->position);
}

// Спеллкарта "Перекрестье" игрока, наводимые пули, движение по спирали
Behaviour_Player_Bullet2::Behaviour_Player_Bullet2(CL_Rectf clipping_rect, float d_angle, float d_speed)
{
	visible_rect = clipping_rect;
	dangle = d_angle;
	dspeed = d_speed;
}

bool Behaviour_Player_Bullet2::Update(Bullet *p_bullet, float time)
{
	// Здесь линейная и угловая скорости
	// p_bullet->speed			- линейная
	// p_bullet->param_float1	- угловая
	// Центр хранится в p_bullet->start_position
	// Коррекция скорости при удалении от центра?
	// p_bullet->active работает триггером
	p_bullet->time_lived += time;
	if (p_bullet->active == 1)
	{
		// начальное движение  по спирали
		float r = p_bullet->time_lived * p_bullet->speed / 1000.0f;
		float a = p_bullet->time_lived * p_bullet->param_float1 / 1000.0f;
		if (abs(a) > dangle)
		{
			p_bullet->active = 2;
			float t = 1000.0f * dangle / abs(p_bullet->param_float1);
			r = t * p_bullet->speed / 1000.0f;
			float dtime = p_bullet->time_lived - t;
			CL_Angle an((a<0?-1:1) * dangle + p_bullet->start_param_float1, cl_degrees);
			p_bullet->position = p_bullet->start_position + CL_Vec2<float>(r * cos(an.to_radians()), r * sin(an.to_radians()));
			if (a > 0)
				p_bullet->orientation = an + CL_Angle(atan(an.to_radians()), cl_radians);
			else
			{
				CL_Angle a1(dangle + p_bullet->start_param_float1, cl_degrees);
				CL_Angle a2(atan(a1.to_radians()), cl_radians);
				p_bullet->orientation = an - a2;
			}
			p_bullet->direction = CL_Vec2<float>(cos(p_bullet->orientation.to_radians()), sin(p_bullet->orientation.to_radians()));
			//CL_Vec2<float> pos2 = p_bullet->start_position + CL_Vec2<float>(r * cos(CL_Angle(a + p_bullet->start_param_float1, cl_degrees).to_radians()), r * sin(CL_Angle(a + p_bullet->start_param_float1, cl_degrees).to_radians()));
			//p_bullet->direction = (pos2 - p_bullet->position).normalize();
			p_bullet->position += p_bullet->direction * p_bullet->speed * dspeed * dtime / 1000.0f;
		}
		else
		{
			CL_Angle an(a + p_bullet->start_param_float1, cl_degrees);
			p_bullet->position = p_bullet->start_position + CL_Vec2<float>(r * cos(an.to_radians()), r * sin(an.to_radians()));
			p_bullet->direction = (p_bullet->position - p_bullet->prev_position).normalize();
		}
	}
	else
	{
		// Выход на прямую
		p_bullet->position += p_bullet->direction * p_bullet->speed * dspeed * time / 1000.0f;
	}
	return InsideScreen(p_bullet->position);
}


// Вертящаяся в полёте пуля
Behaviour_Spinning::Behaviour_Spinning(CL_Rectf clipping_rect)
{
	visible_rect = clipping_rect;
}

bool Behaviour_Spinning::Update(Bullet *p_bullet, float time)
{
	p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	p_bullet->orientation -= CL_Angle(p_bullet->param_float1 * time, cl_degrees);
	return InsideScreen(p_bullet->position);
}


// ============   Контролируемые паттернами =====================

// Движется определённое время, после чего останавливается.
// По триггеру паттерна изменяет скорость и направление
Behaviour_Controlled1::Behaviour_Controlled1(CL_Rectf clipping_rect)
{
	visible_rect = clipping_rect;
}

bool Behaviour_Controlled1::Update(Bullet *p_bullet, float time)
{
	p_bullet->time_lived += time;
	if (p_bullet->use_pattern > 1)
	{
		// Уже вторая фаза
		p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	}
	else if (p_bullet->pattern->trigger)
	{
		// сработал триггер паттерна на изменение скорости и направления
		p_bullet->speed = p_bullet->param_float1;
		p_bullet->direction = p_bullet->start_position;
		p_bullet->orientation = patterns.get_bullet_orientation(p_bullet->direction);
		p_bullet->use_pattern = 2;
	}
	else if (p_bullet->time_lived > p_bullet->start_param_float1)
	{
		p_bullet->speed = 0;
	}
	else
		p_bullet->position += p_bullet->direction * p_bullet->speed * time;
	return InsideScreen(p_bullet->position);
}

// ============   Лазоры   =====================

// Простой прямой лазор, истекающий через определённое время
// param_float1 - время действия
Behaviour_Lazor::Behaviour_Lazor()
{
}

bool Behaviour_Lazor::Update(Bullet *p_bullet, float time)
{
	if (p_bullet->time_lived > p_bullet->param_float1)
		return false;
	else
		return true;
}



// ================================================================
// ===                              Пути следования врагов
// ================================================================

// Враги просто движутся по прямой
Enemy_Path_Straight::Enemy_Path_Straight(CL_Rectf clipping_rect)
{
	visible_rect = clipping_rect;
}

int Enemy_Path_Straight::Update(Enemy *p_enemy, float time)
{
	p_enemy->position += p_enemy->move * p_enemy->speed * time;
	p_enemy->traveled += p_enemy->speed * time;
	//if ((p_enemy->position.y > visible_rect.top) && (p_enemy->position.x > visible_rect.left))
	//	if (!InsideScreen(CL_Rectf(p_enemy->position.x - p_enemy->sprite_size.x / 2, p_enemy->position.y - p_enemy->sprite_size.y / 2, p_enemy->position.x + p_enemy->sprite_size.x / 2, p_enemy->position.y + p_enemy->sprite_size.y / 2)))
	//		return 0;
	// Включим триггеры на пути
	if (p_enemy->trigger == 0)
	{
		if (p_enemy->position.y > 10)
			p_enemy->trigger = 10;
	}
	else if (p_enemy->trigger == 10)
	{
		if (p_enemy->position.y > 100)
			p_enemy->trigger = 20;
	}
	else if (p_enemy->trigger == 20)
	{
		if (p_enemy->position.y > 150)
			p_enemy->trigger = 30;
	}
	//return p_enemy->alive;
	return InsideScreen(p_enemy->position);
}

// Доходят до определённой высоты и заворачивают в сторону
// orientation - куда заворачивать: 0 - вправо, 1 - влево
Enemy_Path_Move_Aside::Enemy_Path_Move_Aside(CL_Rectf clipping_rect, float pscreen, bool orientation)
{
	visible_rect = clipping_rect;
	y = visible_rect.top + visible_rect.get_height() * pscreen;
	left = orientation;
}

int Enemy_Path_Move_Aside::Update(Enemy *p_enemy, float time)
{
	p_enemy->traveled += p_enemy->speed * time;
	if (p_enemy->position.y > y)
	{
		// Перешли в закругление и в сторону
		if (left)
		{
			if (p_enemy->orientation > CL_Angle(180, cl_degrees))
				p_enemy->position += p_enemy->move * p_enemy->speed * time;
			else
			{
				p_enemy->orientation += CL_Angle(p_enemy->speed * time * 0.3f , cl_degrees);
				if (p_enemy->orientation > CL_Angle(180, cl_degrees))
					p_enemy->move = CL_Vec2<float>(cos(CL_Angle(180, cl_degrees).to_radians()), sin(CL_Angle(180, cl_degrees).to_radians()));
				p_enemy->move = CL_Vec2<float>(cos(p_enemy->orientation.to_radians()), sin(p_enemy->orientation.to_radians()));
				p_enemy->position += p_enemy->move * p_enemy->speed * time;
			}
		}
		else
		{
			if (p_enemy->orientation < CL_Angle(0, cl_degrees))
				p_enemy->position += p_enemy->move * p_enemy->speed * time;
			else
			{
				p_enemy->orientation -= CL_Angle(p_enemy->speed * time * 0.3f , cl_degrees);
				if (p_enemy->orientation < CL_Angle(0, cl_degrees))
					p_enemy->move = CL_Vec2<float>(cos(CL_Angle(0, cl_degrees).to_radians()), sin(CL_Angle(0, cl_degrees).to_radians()));
				p_enemy->move = CL_Vec2<float>(cos(p_enemy->orientation.to_radians()), sin(p_enemy->orientation.to_radians()));
				p_enemy->position += p_enemy->move * p_enemy->speed * time;
			}
		}
		if (p_enemy->position.y > visible_rect.top)
			if (!InsideScreen(CL_Rectf(p_enemy->position.x - p_enemy->sprite_size.x / 2, p_enemy->position.y - p_enemy->sprite_size.y / 2, p_enemy->position.x + p_enemy->sprite_size.x / 2, p_enemy->position.y + p_enemy->sprite_size.y / 2)))
				return 0;
	}
	else
	{
		// Движемся прямо
		p_enemy->position += p_enemy->move * p_enemy->speed * time;
		if (p_enemy->position.y > visible_rect.top)
			if (!InsideScreen(CL_Rectf(p_enemy->position.x - p_enemy->sprite_size.x / 2, p_enemy->position.y - p_enemy->sprite_size.y / 2, p_enemy->position.x + p_enemy->sprite_size.x / 2, p_enemy->position.y + p_enemy->sprite_size.y / 2)))
				return 0;
	}
	// Включим триггеры на пути
	if (p_enemy->trigger == 0)
	{
		if (p_enemy->position.y > 10)
			p_enemy->trigger = 10;
	}
	else if (p_enemy->trigger == 10)
	{
		if (p_enemy->position.y > 100)
			p_enemy->trigger = 20;
	}
	else if (p_enemy->trigger == 20)
	{
		if (p_enemy->position.y > 150)
			p_enemy->trigger = 30;
	}
	return p_enemy->alive;
}

// Доходит до определённой высоты, топчется на месте, потом улетает в сторону
Enemy_Path_Stop_Wait_Move::Enemy_Path_Stop_Wait_Move(CL_Rectf clipping_rect, float wtime)
{
	visible_rect = clipping_rect;
	wait_time = wtime;
}

int Enemy_Path_Stop_Wait_Move::Update(Enemy *p_enemy, float time)
{
	// определяем фазу по триггеру
	if (p_enemy->trigger == 0)
	{
		// Начальное движение
		p_enemy->traveled += p_enemy->speed * time;
		p_enemy->position += p_enemy->move * p_enemy->speed * time;
		if (p_enemy->position.y > visible_rect.top + p_enemy->param_float1)
		{
			// Дошли до места, останавливаемся
			p_enemy->position.y = visible_rect.top + p_enemy->param_float1;
			p_enemy->trigger = 10;
			p_enemy->param_float1 = p_enemy->time_lived;
			//p_enemy->orientation = CL_Angle((float)(rand()%360), cl_degrees);
			//p_enemy->move = CL_Vec2<float>(cos(p_enemy->orientation.to_radians()), sin(p_enemy->orientation.to_radians()));
			p_enemy->speed = 0.001f;
		}
	}
	else if (p_enemy->trigger < 30)
	{
		// Топчемся на месте
		p_enemy->position += p_enemy->move * p_enemy->speed * time;
		// Переключаем триггер
		if (p_enemy->time_lived > p_enemy->param_float1 + wait_time / 10.0f)
			p_enemy->trigger = 11;
		if (p_enemy->time_lived > p_enemy->param_float1 + wait_time / 5.0f )
			p_enemy->trigger = 12;
		if (p_enemy->time_lived > p_enemy->param_float1 + wait_time / 4.0f)
			p_enemy->trigger = 13;
		if (p_enemy->time_lived > p_enemy->param_float1 + wait_time / 2.0f)
			p_enemy->trigger = 20;
		if (p_enemy->time_lived > p_enemy->param_float1 + wait_time)
		{
			// Время истекло, убегаем
			p_enemy->trigger = 30;
			p_enemy->orientation = CL_Angle(180 + ((p_enemy->position.x - global_state.game_field.left) / global_state.game_field.get_width())*180, cl_degrees);
			p_enemy->move = CL_Vec2<float>(cos(p_enemy->orientation.to_radians()), sin(p_enemy->orientation.to_radians()));
			p_enemy->speed = p_enemy->base_speed / 10;
		}
	}
	else
	{
		if (p_enemy->time_lived > (p_enemy->param_float1 + wait_time + 2000))
			p_enemy->speed = p_enemy->base_speed;
		else
			p_enemy->speed = p_enemy->base_speed * (p_enemy->time_lived - (p_enemy->param_float1 + wait_time)) / 2000;
		p_enemy->position += p_enemy->move * p_enemy->speed * time;
		p_enemy->traveled += p_enemy->speed * time;
		if (!InsideScreen(CL_Rectf(p_enemy->position.x - p_enemy->sprite_size.x / 2, p_enemy->position.y - p_enemy->sprite_size.y / 2, p_enemy->position.x + p_enemy->sprite_size.x / 2, p_enemy->position.y + p_enemy->sprite_size.y / 2)))
			return 0;
	}
	return p_enemy->alive;
}

// Доходит до определённой высоты, останавливается, потом продолжает движение
Enemy_Path_Go_Wait_Go::Enemy_Path_Go_Wait_Go(CL_Rectf clipping_rect, float wtime)
{
	visible_rect = clipping_rect;
	wait_time = wtime;
}

int Enemy_Path_Go_Wait_Go::Update(Enemy *p_enemy, float time)
{
	p_enemy->traveled += p_enemy->speed * time;
	// определяем фазу по триггеру
	if (p_enemy->trigger == 0)
	{
		// Начальное движение
		if ((p_enemy->position.y > visible_rect.top + p_enemy->param_float1 - 50) && p_enemy->speed > 0.05f)
		{
			p_enemy->speed = p_enemy->speed * (0.9f + 1/time);
		}
		p_enemy->position += p_enemy->move * p_enemy->speed * time;
		if (p_enemy->position.y > visible_rect.top + p_enemy->param_float1)
		{
			// Дошли до места, останавливаемся
			p_enemy->position.y = visible_rect.top + p_enemy->param_float1;
			p_enemy->trigger = 10;
			p_enemy->param_float1 = p_enemy->time_lived;
			p_enemy->speed = 0.0f;
		}
	}
	else if (p_enemy->trigger < 30)
	{
		// Стоим на месте
		// Переключаем триггер на половине времени ожидания
		if (p_enemy->time_lived > p_enemy->param_float1 / 2.0f + wait_time)
			p_enemy->trigger = 20;
		if (p_enemy->time_lived > p_enemy->param_float1 + wait_time)
		{
			// Время истекло, двигаем дальше
			p_enemy->trigger = 30;
			p_enemy->speed = p_enemy->base_speed / 10;
		}
	}
	else
	{
		if (p_enemy->time_lived > (p_enemy->param_float1 + wait_time + 2000))
			p_enemy->speed = p_enemy->base_speed;
		else
			p_enemy->speed = p_enemy->base_speed * (p_enemy->time_lived - (p_enemy->param_float1 + wait_time)) / 2000;
		p_enemy->position += p_enemy->move * p_enemy->speed * time;
		if (!InsideScreen(CL_Rectf(p_enemy->position.x - p_enemy->sprite_size.x / 2, p_enemy->position.y - p_enemy->sprite_size.y / 2, p_enemy->position.x + p_enemy->sprite_size.x / 2, p_enemy->position.y + p_enemy->sprite_size.y / 2)))
			return 0;
	}
	return p_enemy->alive;
}

// Движение до одной точки с остановкой, потом движение дальше, движения плавные
Enemy_Path_One_Point_Stop::Enemy_Path_One_Point_Stop(CL_Rectf clipping_rect, float wtime)
{
	visible_rect = clipping_rect;
	wait_time = wtime;
}

int Enemy_Path_One_Point_Stop::Update(Enemy *p_enemy, float time)
{
	p_enemy->traveled += p_enemy->speed * time;
	CL_Vec2f v0(350, 30);
	CL_Vec2f v1(200, 250);
	CL_Vec2f v2(-50, 0);
	float t1 = 2000;
	float t2 = 3000;
	// определяем фазу по триггеру
	if (p_enemy->trigger == 0)
	{
		// Начальное движение
		//if (p_enemy->position.distance(v1) < 1.0f || p_enemy->position.distance(v1) > p_enemy->prev_position.distance(v1))
		//{
		//	p_enemy->position = v1;
		//	p_enemy->trigger = 1;
		//	p_enemy->param_float2 = p_enemy->time_lived;
		//}
		//else
		//{
		//	p_enemy->position = patterns.get_move_position1(v0, v1, p_enemy->speed, p_enemy->param_float1, p_enemy->time_lived);
		//}
		if (p_enemy->time_lived > t1)
		{
			p_enemy->position = v1;
			p_enemy->trigger = 1;
		}
		else
		{
			p_enemy->position = patterns.get_move_position2(v0, v1, t1, 0.3f, p_enemy->time_lived);
		}
	}
	else if (p_enemy->trigger == 1)
	{
		// Стоим на месте
		//if (p_enemy->time_lived > (p_enemy->param_float2 + 1000))
		//{
		//	p_enemy->trigger = 2;
		//	p_enemy->param_float2 = p_enemy->time_lived;
		//}
		if (p_enemy->time_lived > (t1 + 1000))
		{
			p_enemy->trigger = 2;
		}
	}
	else
	{
		p_enemy->position = patterns.get_move_position2(v1, v2, t2, 0.3f, p_enemy->time_lived - (t1 + 1000));
	}
	return InsideScreen(p_enemy->position);
}

// Синусоидальное движение в заданном направлении
// enemy->orientation - Направление
// enemy->param1 - смещение
// scale - шкала частоты движения
// Движение не по фиксированной траектории, а по синусоидальному направлению
Enemy_Path_Sinus::Enemy_Path_Sinus(float s)
{
	scale = s;
}

int Enemy_Path_Sinus::Update(Enemy *p_enemy, float time)
{
	p_enemy->traveled += p_enemy->speed * time;
	int a = (int)(p_enemy->traveled / scale + p_enemy->param_float1) % 120;
	if (a > 60)
		a = 120 - a;
	CL_Angle move(a - 30, cl_degrees);
	move += p_enemy->orientation;
	p_enemy->move = CL_Vec2f(cos(move.to_radians()), sin(move.to_radians()));
	p_enemy->position += p_enemy->move * p_enemy->speed * time;
	return InsideScreen(p_enemy->position);
}

// ------------------------------------------------------------------------------------------
// -                     Поведение боссов
// ------------------------------------------------------------------------------------------
Enemy_Path_Boss::Enemy_Path_Boss(CL_Rectf clipping_rect, float y)
{
	h = y;
}

int Enemy_Path_Boss::Update(Enemy *p_enemy, float time)
{
	EnemyBoss *b_enemy;
	if (p_enemy->is_boss)
		b_enemy = static_cast<EnemyBoss*> (p_enemy);
	else
		return 0; // wut
	b_enemy->ukanderu += time;
	if (b_enemy->acting)
		b_enemy->act_time +=time;
	if (global_state.game_dialog)
		return 1;
	// определяем фазу по триггеру
	if (b_enemy->trigger == 0)
	{
		// Начальные установки
		b_enemy->acting = 0;
		b_enemy->moving = 0;
		b_enemy->ukanderu = 0;
		// Начальное движение
		if ((b_enemy->position.y > visible_rect.top + h - 50) && b_enemy->speed > 0.05f)
		{
			b_enemy->speed = b_enemy->speed * (0.9f + 1/time);
		}
		b_enemy->position += b_enemy->move * b_enemy->speed * time;
		if (b_enemy->position.y > visible_rect.top + h)
		{
			// Дошли до места, останавливаемся
			b_enemy->position.y = visible_rect.top + h;
			b_enemy->trigger = 10;
			b_enemy->param_float1 = b_enemy->time_lived;
			b_enemy->speed = 0.0f;
		}
		b_enemy->rnd_rings_center = b_enemy->position;
		// Инициализировать имена спеллкарт
		b_enemy->spellcard_name_effect.active = 0;
		b_enemy->spellcard_name_effect.appearance = renders.GetEffectRender("spellcard_name");
		b_enemy->spellcard_name_effect.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		b_enemy->spellcard_name_effect.param_int = 0;
	}
	else if (b_enemy->trigger == 10)
	{
		// Начальное действие - диалог и пр.
		if (b_enemy->have_dialog)
		{
			// Запустить в действие диалог босса
			global_state.game_dialog = 1;
			CL_String dialog_name = b_enemy->dialog_name;
			if (global_state.lang == 0)
				dialog_name += "_rus";
			if (global_state.lang == 1)
				dialog_name += "_jap";
			dialogs.StartDialog(dialog_name);
			cl_log_event("Game Event", "Start Boss Dialog %1", dialog_name);
		}
		b_enemy->current_spellcard = 0;
		b_enemy->current_spellcard_action = 0;
		b_enemy->spell_hp = b_enemy->spell_patterns[0].nonspellhp;
		b_enemy->current_spell_level = 0; // Запускаем первый нонспелл
		b_enemy->spell_time = static_cast<float>(patterns.GetSpellcard(b_enemy->spell_patterns[0].nonspell)->spellcard_time);
		b_enemy->action_time = 0;
		b_enemy->acceleration = 0.001f;
		b_enemy->trigger = 30; // Занять исходную позицию перед спеллкартой
		b_enemy->spell_effects_processed = 0;
		b_enemy->position1 = b_enemy->position;
		b_enemy->param_float1 = (b_enemy->position1 - b_enemy->spell_patterns[0].start_position).length() / b_enemy->spell_patterns[0].start_speed; // Время движения
		global_state.game_boss_spellcard_active = 0;
		player.spell_failed = 0;
		// Показать имя босса
		Effect boss_name;
		if (b_enemy->name == "letty")
			boss_name.param_int = 0;
		else
			boss_name.param_int = 1;
		boss_name.position = CL_Vec2f(32, 36);
		boss_name.appearance = renders.GetEffectRender("boss_name");
		boss_name.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
		effects.Create(boss_name);
	}
	else if (b_enemy->trigger == 20)
	{
		// Босс под управлением спеллкарты
		int spellwork;
		if (b_enemy->current_spell_level)
		{
			spellwork = patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].spell)->Update(b_enemy, time);
			b_enemy->spellcard_name_effect.Update(time); // Ох, какая по-дурацки и запутанно с этим получилось
		}
		else
			spellwork = patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].nonspell)->Update(b_enemy, time);
		if (spellwork)
		{
			// Спеллкарта завершена, переходим к следующей
			global_state.game_boss_spellcard_active = 0;
			b_enemy->spellcard_name_effect.active = 0;
			b_enemy->acting = 0;
			b_enemy->act_time = 0;
			if (spellwork == 1)
			{
				// Закончились жизни спеллкарты
				if (b_enemy->current_spell_level)
				{
					// Полноценная спеллкарта, не нонспелл, высчитать бонусы
					if (player.spell_failed)
					{
						// Спеллкарта зафейлена
						// Запустить эффект фейла
						Effect eff;
						eff.appearance = renders.GetEffectRender("spellcard_result");
						eff.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
						eff.param_int = 0;
						eff.position = CL_Vec2f(224,240);
						effects.Create(eff);
					}
					else
					{
						// Спеллкарта получена, посчитать бонус
						int bns = b_enemy->spell_patterns[b_enemy->current_spellcard].price;
						player.score += bns;
						// Запустить эффект с количеством полученных очков
						Effect eff;
						eff.appearance = renders.GetEffectRender("spellcard_result");
						eff.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
						eff.param_int = bns;
						eff.position = CL_Vec2f(224,240);
						effects.Create(eff);
					}
				}
			}
			else
			{
				// Тамаут спеллкарты
				// Запустить эффект фейла
				Effect eff;
				eff.appearance = renders.GetEffectRender("spellcard_result");
				eff.behaviour = patterns.GetEffectBehaviour("effect_stay_still");
				eff.param_int = 0;
				eff.position = CL_Vec2f(224,240);
				effects.Create(eff);
			}
			// Отключить все пули на экране и получить с них бонус
			bmanager.CancelBossBullets(spellwork);
			// Выключить все неотработавшие паттерны
			patterns.Clear();
			// Отключить фоновые эффекты
			Spellcard_Work *spl;
			if (b_enemy->current_spell_level)
				spl = patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].spell);
			else
				spl = patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].nonspell);
			for (int i = 0; i < spl->spellcard_effects.size(); i++)
			{
				_Spellcard_Effect eff = spl->spellcard_effects[i];
				if (eff.type == 1)
				{
					// ================== TO DO ==================
					if (eff.param_int == 1)
						boss_background.hide_layer("letty_back_1", 200);
				}
			}
			// ---
			if (b_enemy->current_spell_level)
			{
				// Спеллкарта пройдена, перейти к следующей
				b_enemy->current_spellcard++;
				if (b_enemy->current_spellcard >= b_enemy->shots)
				{
					// Последняя спеллкарта - босс уничтожен, а мидбосс ушёл
					if ((spellwork == 1) || (b_enemy->name != "noname"))
					{
						// Уничтожение
						b_enemy->alive = 0;
						b_enemy->dead = 1;
						// Эффекты уничтожения
						// ================== TO DO ==================
					}
					else
					{
						// Таймаут мидбосса
						b_enemy->trigger = 40;
						b_enemy->move = (b_enemy->position - CL_Vec2<float>(280.0f, 600.0f)).normalize();
					}
					global_state.game_boss_spellcard_active = 0;
					global_state.game_boss_battle = 0;
				}
				else
				{
					b_enemy->current_spell_level = 0; // Запустить нонспелл следующей
					b_enemy->current_spellcard_action = 0;
					b_enemy->spell_hp = b_enemy->spell_patterns[b_enemy->current_spellcard].nonspellhp;
					b_enemy->spell_time = static_cast<float>(patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].nonspell)->spellcard_time);
					b_enemy->action_time = 0;
					b_enemy->position1 = b_enemy->position;
					b_enemy->param_float1 = (b_enemy->position1 - b_enemy->spell_patterns[b_enemy->current_spellcard].start_position).length() / b_enemy->spell_patterns[b_enemy->current_spellcard].start_speed; // Время движения
					global_state.game_boss_spellcard_active = 0;
					player.spell_failed = 0;
					b_enemy->trigger = 30; // Занять исходную позицию перед спеллкартой
					b_enemy->spell_effects_processed = 0;
				}
			}
			else
			{
				// Отработал нонспелл, запустить следующей спеллкарту
				b_enemy->current_spell_level = 1;
				// spellcard
				b_enemy->current_spellcard_action = 0;
				b_enemy->spell_hp = b_enemy->spell_patterns[b_enemy->current_spellcard].spellhp;
				b_enemy->spell_time = static_cast<float>(patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].spell)->spellcard_time);
				b_enemy->action_time = 0;
				b_enemy->position1 = b_enemy->position;
				b_enemy->param_float1 = (b_enemy->position1 - b_enemy->spell_patterns[b_enemy->current_spellcard].start_position).length() / b_enemy->spell_patterns[b_enemy->current_spellcard].start_speed; // Время движения
				global_state.game_boss_spellcard_active = 0;
				player.spell_failed = 0;
				b_enemy->trigger = 30; // Занять исходную позицию перед спеллкартой
				b_enemy->spell_effects_processed = 0;
			}
		}
	}
	else if (b_enemy->trigger == 30)
	{
		// Передислокация и ожидание перед следующей спеллкартой
		b_enemy->action_time += time; //  использовать action_time для текущего отсчёта времени
		if (b_enemy->action_time > b_enemy->param_float1)
		{
			b_enemy->position = b_enemy->spell_patterns[b_enemy->current_spellcard].start_position;
			b_enemy->position1 = b_enemy->position;
			b_enemy->action_time = 0;
			b_enemy->current_spellcard_action = 0;
			b_enemy->param_float1 = b_enemy->spell_patterns[b_enemy->current_spellcard].start_wait;
			b_enemy->trigger = 31; // Ожидание перед спеллкартой
		}
		else
		{
			CL_Vec2<float> v = b_enemy->spell_patterns[b_enemy->current_spellcard].start_position - b_enemy->position1;
			b_enemy->position = b_enemy->position1 + v * (b_enemy->action_time / b_enemy->param_float1);
		}
		// Запуск эффектов босса на спеллкарте
		if (!b_enemy->spell_effects_processed)
		{
			cl_log_event("Debug", "Process spellcard effects");
			Spellcard_Work *spl;
			if (b_enemy->current_spell_level)
				spl = patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].spell);
			else
				spl = patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].nonspell);
			b_enemy->spell_effects_processed = 1;
			// В ходе обработки эффектов подсчитываем количество колец на боссе
			int bbrings[] = {255,255,255,255,255,255,255,255};
			int bbrings_count = 0;
			for (int i = 0; i < spl->spellcard_effects.size(); i++)
			{
				_Spellcard_Effect eff = spl->spellcard_effects[i];
				if (eff.type == 0)
				{
					// кольцо
					bbrings[bbrings_count] = eff.param_int;
					if (++bbrings_count > 7)
						bbrings_count = 7; // От дурака
					int not_found = 1;
					for ( int j = 0; j < b_enemy->rnd_rings; j++)
					{
						if (b_enemy->rnd_ring_index[j] == eff.param_int)
						{
							// Передислоцируем старое кольцо
							cl_log_event("Debug", "Relocate boss ring");
							not_found = 0;
							b_enemy->rnd_ring_status[j] = 2;
							b_enemy->rnd_ring_temp[j] = eff.param1;
							b_enemy->rnd_ring_width[j] = eff.param2;
							b_enemy->rnd_ring_speed[j] = eff.param3;
						}
					}
					if (not_found)
					{
						// Новое кольцо
						cl_log_event("Debug", "Add boss ring");
						b_enemy->rnd_rings++;
						b_enemy->rnd_ring_status[b_enemy->rnd_rings - 1] = 0;
						b_enemy->rnd_ring_index[b_enemy->rnd_rings - 1] = eff.param_int;
						b_enemy->rnd_ring_radius[b_enemy->rnd_rings - 1] = 10.0f;
						b_enemy->rnd_ring_temp[b_enemy->rnd_rings - 1] = eff.param1;
						b_enemy->rnd_ring_width[b_enemy->rnd_rings - 1] = eff.param2;
						b_enemy->rnd_ring_speed[b_enemy->rnd_rings - 1] = eff.param3;
						b_enemy->rnd_ring_alpha[b_enemy->rnd_rings - 1] = 0.01f;
						b_enemy->rnd_ring_position[b_enemy->rnd_rings - 1] = 0;
					}
				}
			}
			// Теперь проверим количество колец на боссе и выкинем лишние
			if (b_enemy->rnd_rings > bbrings_count)
			{
				for ( int j = 0; j < b_enemy->rnd_rings; j++)
				{
					int not_found = 1;
					for (int h=0; h<bbrings_count; h++)
					{
						if (bbrings[h] == b_enemy->rnd_ring_index[j])
							not_found = 0;
					}
					if (not_found)
					{
						// Выкидываем кольцо
						cl_log_event("Debug", "Remove boss ring");
						b_enemy->rnd_ring_status[j] = 3;
						b_enemy->rnd_ring_temp[j] = 600.0f;
					}
				}
			}
		}
	}
	else if (b_enemy->trigger == 31)
	{
		// Ожидание перед запуском спеллкарты
		b_enemy->action_time += time; //  использовать action_time для текущего отсчёта времени
		if (b_enemy->action_time > b_enemy->param_float1)
		{
			// Запуск самой спеллкарты
			b_enemy->current_spellcard_action = 0;
			if (b_enemy->current_spell_level)
			{
				b_enemy->spell_hp = b_enemy->spell_patterns[b_enemy->current_spellcard].spellhp;
				b_enemy->spell_time = static_cast<float>(patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].spell)->spellcard_time);
			}
			else
			{
				b_enemy->spell_hp = b_enemy->spell_patterns[b_enemy->current_spellcard].nonspellhp;
				b_enemy->spell_time = static_cast<float>(patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].nonspell)->spellcard_time);
			}
			b_enemy->action_time = 0;
			b_enemy->trigger = 20; // Перейти к работе спеллкарты
			global_state.game_boss_spellcard_active = 1;
			// Запуск эффектов спеллкарты
			cl_log_event("Debug", "Launch spellcard effects");
			Spellcard_Work *spl;
			if (b_enemy->current_spell_level)
				spl = patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].spell);
			else
				spl = patterns.GetSpellcard(b_enemy->spell_patterns[b_enemy->current_spellcard].nonspell);
			for (int i = 0; i < spl->spellcard_effects.size(); i++)
			{
				_Spellcard_Effect eff = spl->spellcard_effects[i];
				if (eff.type == 1)
				{
					// Запуск фона спеллкарты
					if (eff.param_int == 1)
						boss_background.show_layer("letty_back_1", 200);
				}
			}
			if (b_enemy->current_spell_level)
			{
				// Запустить эффект названия спеллкарты
				b_enemy->spellcard_name_effect.time_lived = 0;
				b_enemy->spellcard_name_effect.active = 1;
				b_enemy->spellcard_name_effect.param_int = b_enemy->current_spellcard;
			}
		}
	}
	else
	{
		// Уход с поля боя
		b_enemy->position += b_enemy->move * b_enemy->speed * time;
		return InsideScreen(CL_Rectf(b_enemy->position.x - b_enemy->sprite_size.x / 2, b_enemy->position.y - b_enemy->sprite_size.y / 2, b_enemy->position.x + b_enemy->sprite_size.x / 2, b_enemy->position.y + b_enemy->sprite_size.y / 2));
	}
	// Движение центра крутящихся колец вслед за боссом
	if (b_enemy->position != b_enemy->rnd_rings_center)
	{
		CL_Vec2f drct = b_enemy->position - b_enemy->rnd_rings_center;
		CL_Vec2f drct_n = drct.normalize();
		float ring_speed = 0.05f;
		if ((b_enemy->rnd_rings_center + drct_n * ring_speed * time - b_enemy->position).length() > drct.length())
			b_enemy->rnd_rings_center = b_enemy->position;
		else
			b_enemy->rnd_rings_center += drct_n * ring_speed * time;
	}
	return b_enemy->alive;
}

// ------------------------------------------------------------------------------------------
// -                     Спеллкарты
// ------------------------------------------------------------------------------------------
Spellcard_Work::Spellcard_Work(CL_ResourceManager &resources, CL_String name)
{
	// Инициализация спелла
	spellcard_actions.clear();
	spellcard_effects.clear();

	// Действия
	CL_DomElement element = resources.get_resource(name).get_element();
	spellcard_time = CL_StringHelp::text_to_int(element.get_attribute("time")) * 1000;
	std::vector<CL_DomNode> action_nodes = element.select_nodes("action");
	for (size_t i = 0; i < action_nodes.size(); i++)
	{
		CL_DomElement action_element = action_nodes[i].to_element();
		_Spellcard_Action action;
		if (action_element.get_attribute("name") == "move")
		{
			action.type = 0;
			std::vector<CL_String> params = CL_StringHelp::split_text(action_element.get_text(), ";");
			action.x = CL_StringHelp::text_to_int(params[0]);
			action.y = CL_StringHelp::text_to_int(params[1]);
		}
		else if (action_element.get_attribute("name") == "wait")
		{
			action.type = 1;
			action.x = CL_StringHelp::text_to_int(action_element.get_text());
		}
		else if (action_element.get_attribute("name") == "spell")
		{
			PushSpellActions(resources, action_element.get_text());
		}
		else if (action_element.get_attribute("name") == "pattern")
		{
			action.type = 2;
			action.x = CL_StringHelp::text_to_int(action_element.get_text());
		}
		else if (action_element.get_attribute("name") == "speed")
		{
			action.type = 3;
			action.x = CL_StringHelp::text_to_int(action_element.get_text());
		}
		else if (action_element.get_attribute("name") == "accel")
		{
			action.type = 4;
			action.x = (int)(CL_StringHelp::text_to_float(action_element.get_text()) * 1000000.0f);
		}
		else if (action_element.get_attribute("name") == "acting")
		{
			if (action_element.get_text() == "start")
				action.type = 5;
			else
				action.type = 6;
		}
		spellcard_actions.push_back(action);
	}

	// Эффекты
	std::vector<CL_DomNode> effect_nodes = element.select_nodes("effect");
	for (size_t i = 0; i < effect_nodes.size(); i++)
	{
		CL_DomElement effect_element = effect_nodes[i].to_element();
		_Spellcard_Effect effect;
		if (effect_element.get_attribute("name") == "ring")
		{
			effect.type = 0;
			std::vector<CL_String> params = CL_StringHelp::split_text(effect_element.get_text(), ";");
			effect.param_int = CL_StringHelp::text_to_int(params[0]); // индекс
			effect.param1 = CL_StringHelp::text_to_float(params[1]); // радиус
			effect.param2 = CL_StringHelp::text_to_float(params[2]); // толщина
			effect.param3 = CL_StringHelp::text_to_float(params[3]); // скорость
		}
		else if (effect_element.get_attribute("name") == "background")
		{
			effect.type = 1;
			effect.param_int = 0;
			CL_String efstr = effect_element.get_text();
			if (efstr == "letty_back_1")
				effect.param_int = 1;
		}
		spellcard_effects.push_back(effect);
	}
}

void Spellcard_Work::PushSpellActions(CL_ResourceManager &resources, CL_String name)
{
	CL_DomElement element = resources.get_resource(name).get_element();
	std::vector<CL_DomNode> action_nodes = element.select_nodes("action");
	for (size_t i = 0; i < action_nodes.size(); i++)
	{
		CL_DomElement action_element = action_nodes[i].to_element();
		_Spellcard_Action action;
		if (action_element.get_attribute("name") == "move")
		{
			action.type = 0;
			std::vector<CL_String> params = CL_StringHelp::split_text(action_element.get_text(), ";");
			action.x = CL_StringHelp::text_to_int(params[0]);
			action.y = CL_StringHelp::text_to_int(params[1]);
		}
		else if (action_element.get_attribute("name") == "wait")
		{
			action.type = 1;
			action.x = CL_StringHelp::text_to_int(action_element.get_text());
		}
		else if (action_element.get_attribute("name") == "pattern")
		{
			action.type = 2;
			action.x = CL_StringHelp::text_to_int(action_element.get_text());
		}
		else if (action_element.get_attribute("name") == "speed")
		{
			action.type = 3;
			action.x = CL_StringHelp::text_to_int(action_element.get_text());
		}
		else if (action_element.get_attribute("name") == "accel")
		{
			action.type = 4;
			action.x = (int)(CL_StringHelp::text_to_float(action_element.get_text()) * 1000000.0f);
		}
		else if (action_element.get_attribute("name") == "acting")
		{
			if (action_element.get_text() == "start")
				action.type = 5;
			else
				action.type = 6;
		}
		spellcard_actions.push_back(action);
	}
}

int Spellcard_Work::Update(EnemyBoss *b_enemy, float time)
{
	if (b_enemy->spell_hp <= 0 )
		return 1; // Выбиты все жизни
	b_enemy->spell_time -= time;
	if (b_enemy->spell_time < 0)
		return 2; // Таймаут спеллкарты
	if (spellcard_actions[b_enemy->current_spellcard_action].type == 0)
	{
		// Move
		// Плавное движение с ускорением и замедлением исходя из текущей скорости и ускорения.
		if (b_enemy->action_time == 0)
		{
			// Начало работы движения, определение точек движения
			b_enemy->position1 = b_enemy->position;
			b_enemy->position2 = CL_Vec2<float>((float)spellcard_actions[b_enemy->current_spellcard_action].x, (float)spellcard_actions[b_enemy->current_spellcard_action].y);
			b_enemy->move = (b_enemy->position2 - b_enemy->position1).normalize();
			b_enemy->orientation = patterns.get_bullet_orientation(b_enemy->move);
			b_enemy->moving = 1;
			b_enemy->move_time = 0;
		}
		b_enemy->action_time += time;
		if (b_enemy->position.distance(b_enemy->position2) < 1.0f || b_enemy->position.distance(b_enemy->position2) > b_enemy->prev_position.distance(b_enemy->position2))
		{
			b_enemy->position = b_enemy->position2;
			b_enemy->action_time = 0;
			b_enemy->moving = 0;
			b_enemy->current_spellcard_action = (b_enemy->current_spellcard_action + 1) % spellcard_actions.size();
		}
		else
		{
			b_enemy->position = patterns.get_move_position1(b_enemy->position1, b_enemy->position2, b_enemy->speed, b_enemy->acceleration, b_enemy->action_time);
			b_enemy->move_time += time;
		}
	}
	else if (spellcard_actions[b_enemy->current_spellcard_action].type == 1)
	{
		// Wait
		b_enemy->action_time += time;
		b_enemy->move = CL_Vec2f(0, 1.0f);
		b_enemy->orientation = CL_Angle(90, cl_degrees);
		if (b_enemy->action_time > (float)spellcard_actions[b_enemy->current_spellcard_action].x)
		{
			b_enemy->action_time = 0;
			b_enemy->current_spellcard_action = (b_enemy->current_spellcard_action + 1) % spellcard_actions.size();
		}
	}
	else if (spellcard_actions[b_enemy->current_spellcard_action].type == 2)
	{
		// Pattern
		patterns.LaunchPattern(b_enemy->position, spellcard_actions[b_enemy->current_spellcard_action].x, b_enemy);
		b_enemy->current_spellcard_action = (b_enemy->current_spellcard_action + 1) % spellcard_actions.size();
	}
	else if (spellcard_actions[b_enemy->current_spellcard_action].type == 3)
	{
		// Change speed
		b_enemy->speed = (float)spellcard_actions[b_enemy->current_spellcard_action].x / 1000.0f;
		b_enemy->current_spellcard_action = (b_enemy->current_spellcard_action + 1) % spellcard_actions.size();
	}
	else if (spellcard_actions[b_enemy->current_spellcard_action].type == 4)
	{
		// Change acceleration
		b_enemy->acceleration = (float)spellcard_actions[b_enemy->current_spellcard_action].x / 1000000.0f;
		b_enemy->current_spellcard_action = (b_enemy->current_spellcard_action + 1) % spellcard_actions.size();
	}
	else if (spellcard_actions[b_enemy->current_spellcard_action].type == 5)
	{
		// Start acting
		b_enemy->acting = 1;
		b_enemy->current_spellcard_action = (b_enemy->current_spellcard_action + 1) % spellcard_actions.size();
	}
	else if (spellcard_actions[b_enemy->current_spellcard_action].type == 6)
	{
		// Stop acting
		b_enemy->acting = 0;
		b_enemy->act_time = 0;
		b_enemy->current_spellcard_action = (b_enemy->current_spellcard_action + 1) % spellcard_actions.size();
	}
	return 0;
}

// ================================================================
// ===                               Системы поведения эффектов
// ================================================================

Effect_Behaviour_Stay_Still::Effect_Behaviour_Stay_Still()
{
}

bool Effect_Behaviour_Stay_Still::Update(Effect *p_effect, float time)
{
	p_effect->time_lived += time;
	return true;
}

Effect_Behaviour_Score_Float::Effect_Behaviour_Score_Float()
{
}

bool Effect_Behaviour_Score_Float::Update(Effect *p_effect, float time)
{
	p_effect->time_lived += time;
	if (p_effect->time_lived > p_effect->speed)
		p_effect->active = 0;
	return true;
}

Effect_Behaviour_Flyoff::Effect_Behaviour_Flyoff()
{
}

bool Effect_Behaviour_Flyoff::Update(Effect *p_effect, float time)
{
	p_effect->time_lived += time;
	p_effect->position += p_effect->direction * p_effect->speed * time;
	return true;
}

Effect_Behaviour_Grav::Effect_Behaviour_Grav(double gr)
{
	g = gr;
}

bool Effect_Behaviour_Grav::Update(Effect *p_effect, float time)
{
	p_effect->time_lived += time;
	p_effect->position += p_effect->direction * p_effect->speed * time;
	p_effect->direction = CL_Vec2f(p_effect->direction.x * p_effect->speed, p_effect->direction.y * p_effect->speed + abs(p_effect->direction.y) * p_effect->speed*g*time).normalize();
	return true;
}
